Archivos QVD: lo que hay dentro, parte 2

En el primer artículo sobre la estructura del archivo QVD, describí la estructura general y me concentré en los metadatos con suficiente detalle. En este artículo, describiré el formato para almacenar información sobre columnas y compartiré mi experiencia en la interpretación de estos datos.


Entonces (recuerde) un archivo QVD corresponde a una tabla relacional, que, como sabe, consiste en filas. Cada fila de la tabla, a su vez, consta de columnas (o campos), y las filas tienen la misma estructura, que puede ser descrita, por ejemplo, por el operador SQL (crear tabla).


En un archivo QVD, una tabla se almacena como dos partes relacionadas indirectamente:


Las tablas de caracteres (mi término) contienen valores únicos para cada columna en la tabla de origen. Es sobre ellos que discutiremos a continuación.


La tabla de filas contiene las filas de la tabla de origen, cada fila almacena los índices de los valores de columna (campo) de la fila en la tabla de símbolos correspondiente. Discutiré la tabla de filas con más detalle en la tercera parte de esta serie.


En el ejemplo de nuestro plato (recuerde, de la primera parte)


SET NULLINTERPRET =<sym>; tab1: LOAD * INLINE [ ID, NAME 123.12,"Pete" 124,12/31/2018 -2,"Vasya" 1,"John" <sym>,"None" ]; 

En este plato:


  • 5 lineas
  • el campo "ID" tiene 4 valores únicos (NULL no se considera un valor, más detalladamente en la tercera parte)
  • el campo "NOMBRE" tiene 5 valores únicos
  • la primera fila de la tabla de filas contendrá los índices 0 y 0, correspondientes a los valores 123.12 y "Pete", respectivamente

Ocasiones especiales


Como regla, las tablas de símbolos se crean para todos los campos de tabla en el archivo QVD. Pero hay matices.


Si el campo tiene un valor, este valor generalmente se almacena en la tabla de símbolos (en este caso, la tabla de símbolos contendrá un registro). Y en la tabla de filas el campo estará ausente (por lo que está claro cuál debería ser el valor de este campo en cada fila ...)


Si el campo no tiene ningún valor (siempre contiene NULL), no se crea ninguna tabla de símbolos en él.


Estos casos especiales se describirán en la tercera parte, cuando lleguemos a las líneas y al algoritmo para su recreación.


Almacenar tablas de caracteres en un archivo


Cada tabla de símbolos se almacena en un archivo QVD como un bloque binario, su desplazamiento (relativo al comienzo del bloque binario) está contenido en el campo Offset de la sección de metadatos de este campo, la longitud (en bytes) está en el campo Longitud de metadatos.


Por lo tanto, la primera tabla de caracteres siempre tendrá un desplazamiento de 0.


Las tablas de símbolos se suceden una tras otra y no están separadas entre sí de ninguna manera.


Estructura de la tabla de caracteres


La tabla de símbolos contiene valores de campo que se suceden sin separadores, cada valor se representa de la siguiente manera:


  • un byte codifica el tipo de campo (lo describiré a continuación)
  • luego viene el valor binario opcional (depende del tipo de campo)
  • seguido de un valor de cadena opcional (depende del tipo de campo)

Las cadenas se almacenan "tal cual" (en la codificación especificada en los metadatos), la cadena termina con un byte cero. La cadena puede tener longitud cero, es decir constan solo de cero bytes.


Los valores binarios se almacenan de acuerdo con las reglas de la arquitectura donde se generó el archivo QVD (desde mi experiencia, simplemente puede leerlos como valores binarios con un ojo en "endian").


Tipos de campo


Toda la variedad de tipos de datos QVD reunidos con tres básicos


  • (1) entero (4 bytes)
  • (2) coma flotante (8 bytes)
  • (4) línea que termina en cero

También hay tipos combinados.


  • (5) un entero seguido de su representación de cadena (4 bytes más una cadena con un byte cero)
  • (6) un número de coma flotante seguido de su representación de cadena (8 bytes más una cadena con un byte cero)

Entre paréntesis he dado los valores numéricos de los "tipos" (el primer byte del valor del campo en la tabla de símbolos).


Una mente inquisitiva preguntará, "¿dónde están los tres?" Esto no es para mí, también tengo muchas preguntas, de los comentarios aquí, como dijo el héroe de Khabensky en la famosa película "Me abstendré ...".


En general, eso es todo, no es difícil, ¿verdad?


Dos observaciones prácticas no muy agradables


El mismo campo puede tener valores de diferentes tipos en la tabla de símbolos (entero, flotante y cadenas). Yo mismo no lo creí hasta que realicé una serie de experimentos ... Lo único que se puede "garantizar" (con la advertencia de la primera parte - nada se puede garantizar) - no puede haber una mezcla de "número" y "número con una cadena" (uno u otro ) Esto es importante, una mente inquisitiva entenderá :-).


Los valores de los campos no numéricos (no los tipos 1 y 2 en la notación anterior) deben leerse en una fila; es imposible ubicarse en el campo número N ... Es comprensible, pero ineficiente (en términos de procesamiento).


Considere nuevamente nuestra etiqueta anterior, la tabla de caracteres del campo ID se verá así (escribo byte / carácter):


  • número 6 (tipo) + 8 bytes (valor flotante 123.12) + 7 bytes (cadena "123.12" con cero bytes)
  • número 5 (tipo) + 4 bytes (valor entero 124) + 4 bytes (cadena "124" con byte cero)
  • número 5 (tipo) + 4 bytes (entero -2) + 3 bytes (cadena "-2" con byte cero)
  • número 5 (tipo) + 4 bytes (entero 1) + 2 bytes (cadena "1" con byte cero)

Un total de 40 bytes (consulte la parte anterior: metadatos, el valor del atributo Longitud para el campo ID).


De la práctica


La tarea práctica (una de), como ya escribí, para mí era recrear la tabla usando el archivo QVD. De lo anterior se deduce (al menos, debería seguir, probé :-)) que a partir de la descripción de la columna (metadatos más datos) es imposible determinar inequívocamente el tipo de campo (uno que, por ejemplo, escriba en la "tabla de creación ...") .


Como mencioné en la primera parte: el 90% de los campos tienen un tipo DESCONOCIDO en los metadatos, las etiquetas tampoco le permiten establecer de forma exclusiva el tipo de campo (no cargaré al lector con los detalles, créanme) ...


Como ser


En mi trabajo, seguí el camino estadístico: analizo un cierto porcentaje de valores de columna y, en función de los resultados, saco una conclusión: qué tipo asignarle. La precisión es bastante satisfactoria, el problema es que necesita analizar (en el caso general) todos los datos ... En mi práctica, me limité al primer 5-10% de los valores de campo.


Si terminamos con los tipos de datos, una mente inquisitiva hará una pregunta razonable: la "tabla de creación" mencionada implica muchos más tipos de datos ...


Diré esto: en los archivos procesados ​​no se encontraron tipos de datos distintos a los enumerados anteriormente. Los archivos correspondían a tablas muy reales de bases de datos reales y contenían todo el espectro de tipos de datos (por ejemplo, incluso encontré blobs ... ¿Por qué están en QVD? Sería mejor escribir comentarios).


Probablemente, para completar la imagen con tipos de datos, debe explicar las fechas y las marcas de tiempo (otros tipos son cuestión de longitud).


Las fechas se presentan en QVD como un número entero: el número de días desde el comienzo de la era (era del clic). Los expertos de QlikView / QlikSense le dirán fácilmente cuándo comenzó (aunque fue el 30 de diciembre de 1899, no pregunte por qué).


Las marcas de tiempo en QVD están representadas por un número de punto flotante que contiene la fecha como se describe arriba, y la hora en la parte fraccionaria (donde .0 corresponde a la hora "00:00:00" y .999999 corresponde a la hora "23:59:59" - ver en más detalle, por ejemplo, aquí ).


Todavía no he profundizado en esta dirección: mis tablas recreadas a partir de QVD contienen tipos enteros y flotantes para campos como "fecha" y "fecha y hora". Alternativamente, puede usar la representación de cadena: para los campos de este tipo, siempre se usa una representación combinada (tipos 5 y 6).


El último (sobre la práctica): al leer archivos grandes, es lógico crear índices para los campos de cadena, lo cual hice. Esto reduce significativamente el tiempo de procesamiento en casos en los que el tamaño de la tabla de símbolos es mucho menor que el número de filas (es decir, un valor aparece más de una vez en el campo de la tabla original).


Para resumir


En este artículo, examinamos el almacenamiento de valores únicos de campos (columnas), vimos que las columnas se almacenan como una secuencia de valores únicos, nos dimos cuenta de que los tipos de datos están mezclados y que solo hay tres tipos (entero, flotante y fila).


A continuación, debemos familiarizarnos con cómo se almacenan las cadenas: la tercera y última parte de una serie de artículos sobre la estructura QVD se dedicará a esto.

Source: https://habr.com/ru/post/455940/


All Articles