EstadoStatus
Qué hace cada verbo en 0.1.0, sin adornos. Los cuatro funcionan; lo que todavía falta está en Lo que viene, anotado como hueco y no como promesa.
What each verb does in 0.1.0, unvarnished. All four work; what is still missing is in What is coming, written down as a gap rather than a promise.
| VerboVerb | Qué haceWhat it does | SalidaExit |
|---|---|---|
export |
Lee una base a un archivo: el esquema, las filas y, por tabla, un conteo y un hash. Reads a database into an archive: the schema, the rows and, per table, a count and a hash. | 0 · 1 |
import |
Restaura un archivo en una base que ya existe. Si está vacía, recibe el esquema del archivo; si ya tiene tablas, recibe una migración que no borra nada. Restores an archive into a database that already exists. If it is empty, it gets the archive's schema; if it already holds tables, it gets a migration that drops nothing. | 0 · 1 |
verify |
Sin --against, comprueba que el archivo sigue íntegro y no toca ningún servidor. Con él, compara el archivo con una base viva, tabla por tabla.
Without --against, checks that the archive is still intact and touches no server. With it, compares the archive with a live database, table by table. |
0 · 1 · 3 |
inspect |
Lee el manifiesto y la lista de entradas sin descomprimir un byte, tanto de un archivo nuestro como de uno de dbdumper. Reads the manifest and the entry list without unpacking a byte, on one of ours and on one of dbdumper's. | 0 · 1 |
El formato, primeroThe format first
Todo cuelga del formato: el manifiesto con un snapshot completo del esquema, las
fases .sql, el JSONL, la tabla de codificación de valores que define la
igualdad, el hash por tabla, y el lector del manifiesto de dbdumper. Vive en
PeopleWorks.SqlArchive.Core, publicado en nuget.org junto a la herramienta, y
su especificación normativa es FORMAT.md.
Everything hangs off the format: the manifest with a full schema snapshot, the
.sql phases, the JSONL, the value-encoding table that defines equality,
the per-table hash, and the reader for dbdumper's manifest. It lives in
PeopleWorks.SqlArchive.Core, published on nuget.org next to the tool, and
FORMAT.md is its normative specification.
Códigos de salidaExit codes
- 0
- El comando hizo lo que dice que hace. En
verify, además, no hubo diferencias.The command did what it says it does. Forverify, it also means no differences were found. - 1
- Lo intentó y no pudo: una ruta mala, un valor rechazado, un archivo ilegible, una base que no se puede abrir, una tabla que no se publicó.It tried and could not: a bad path, a refused value, an unreadable archive, a database that cannot be opened, a table that was not published.
- 2
- Retirado. Quería decir «el verbo está en la ayuda y todavía no está construido». Ya nada lo devuelve, y a propósito no se reusa: un script escrito contra el significado viejo seguiría corriendo y entendería otra cosa.Retired. It meant "the verb is in the help and is not built yet". Nothing returns it now, and it is deliberately not reused: a script written against the old meaning would keep running and mean the wrong thing.
- 3
- Sólo
verify: la comparación corrió hasta el final y los dos lados no coinciden.verifyonly: the comparison ran to the end and the two sides do not match.
3 es distinto de 1 a propósito: encontrar diferencias no es fallar. «La base ya no es lo que dice el archivo» y «no se pudo hacer la comparación» son respuestas diferentes, y un job nocturno no debería tener que leer el mensaje para distinguirlas.
3 is separate from 1 on purpose: finding drift is not failing. "The database is no longer what the archive says" and "the comparison could not be made" are different answers, and a nightly job should not have to read the message to tell them apart.
InstalaciónInstall
Como herramienta de .NET desde nuget.org, como ejecutable del release de GitHub, o compilando el código.
As a .NET tool from nuget.org, as the executable in the GitHub release, or from source.
Herramienta .NET o ejecutable.NET tool or executable
# herramienta global; necesita .NET 9global tool; needs .NET 9
dotnet tool install -g PeopleWorks.SqlArchive.Cli
sqlarchive --version
# o sqlarchive-win-x64.zip del release, descomprimidoor sqlarchive-win-x64.zip from the release, unzipped
.\sqlarchive.exe --versionEl paquete es PeopleWorks.SqlArchive.Cli
y el comando que instala se llama sqlarchive. El zip del
release trae un solo
ejecutable autocontenido para Windows x64, que no necesita .NET instalado.
The package is PeopleWorks.SqlArchive.Cli
and the command it installs is sqlarchive. The zip in the
release holds a single
self-contained executable for Windows x64, which needs no .NET installed.
Compilar desde el códigoBuild from source
git clone https://github.com/peopleworks/SqlArchive.git
cd SqlArchive
dotnet build SqlArchive.sln -c Release
dotnet run --project src/SqlArchive.Cli -- inspect Ventas.sqlarchiveHace falta el SDK de .NET 9. inspect, y verify sin
--against, leen el archivo y nada más, así que funcionan sin SQL Server;
export, import y verify --against necesitan uno.
The .NET 9 SDK is all it takes. inspect, and verify
without --against, read the archive and nothing else, so they work with no SQL
Server; export, import and verify --against need one.
La secuencia habitualThe usual sequence
Exportar, mirar lo que salió, restaurar, y comprobar que lo restaurado es lo que el archivo dice. Un verbo por paso, y el último es el que convierte «terminó sin errores» en «es la misma base».
Export, look at what came out, restore, and check that what was restored is what the archive says. One verb per step, and the last one is what turns "it finished without errors" into "it is the same database".
# 1 la base, a un archivothe database, into an archive
sqlarchive export --source "Server=.;Database=Ventas;Integrated Security=true" --out Ventas.sqlarchive
# 2 qué dice el archivo de sí mismo; no toca el servidorwhat the archive says about itself; touches no server
sqlarchive inspect Ventas.sqlarchive
# 3 la base de destino tiene que existir: import no la creathe destination database has to exist: import does not create it
sqlcmd -S . -E -Q "CREATE DATABASE VentasCopia"
sqlarchive import Ventas.sqlarchive --destination "Server=.;Database=VentasCopia;Integrated Security=true"
# 4 0 coincide · 3 difiere · 1 no se pudo comparar0 matches · 3 differs · 1 could not compare
sqlarchive verify Ventas.sqlarchive --against "Server=.;Database=VentasCopia;Integrated Security=true"Qué deja cada pasoWhat each step leaves behind
- export
- Un archivo que lleva, por tabla, el conteo y el hash de lo que leyó.An archive carrying, per table, the count and the hash of what it read.
- inspect
- Lo que ese archivo afirma: con qué consistencia se leyó, qué tablas, cuántas filas, y si es parcial.What that archive claims: the consistency it was read with, which tables, how many rows, and whether it is partial.
- import
- Cada tabla publicada por sí sola, y sólo si lo que llegó coincide con el manifiesto.Each table published on its own, and only when what arrived matches the manifest.
- verify
- Un veredicto por tabla, leído otra vez desde el destino.A verdict per table, read again from the destination.
La cadena de conexiónThe connection string
--source, --destination, --against. No hay archivo ni variable de entorno. Una contraseña escrita ahí la pueden leer otros procesos de la máquina, y queda en el historial del shell.
In 0.1.0 it is only accepted as an argument: --source, --destination, --against. There is no file and no environment variable. A password written there can be read by other processes on the machine, and it stays in shell history.
Por eso los ejemplos de esta guía usan Integrated Security=true: sin
contraseña no hay nada que filtrar. Pasar la cadena por archivo o por variable está anotado
como hueco en Lo que viene.
That is why the examples in this guide use Integrated Security=true:
with no password there is nothing to leak. Passing the string through a file or a variable is
recorded as a gap in What is coming.
export
Lee una base a un archivo: el esquema completo, las filas de cada tabla y, por tabla, el conteo y el hash de los que después dependen import y verify. Por defecto cada tabla se lee por su cuenta; --consistent las lee todas del mismo instante.
Reads a database into an archive: the full schema, every table's rows and, per table, the count and the hash that import and verify later rely on. By default each table is read on its own; --consistent reads them all from the same instant.
Cómo se correHow to run it
# toda la base, cada tabla por su cuentathe whole database, each table on its own
sqlarchive export --source "Server=.;Database=Ventas;Integrated Security=true" --out Ventas.sqlarchive
# todas las tablas del mismo instante, o un rechazo que dice qué faltaevery table from the same instant, or a refusal that says what is missing
sqlarchive export -s "Server=.;Database=Ventas;Integrated Security=true" -o VentasSnap.sqlarchive --consistent
# parcial: un esquema, y de una tabla sólo algunas filaspartial: one schema, and only some rows of one table
sqlarchive export -s "Server=.;Database=Ventas;Integrated Security=true" -o Pedidos.sqlarchive --table "ventas.*" --where "ventas.Pedido=Total>0"Lo que imprimeWhat it prints
── Ventas.sqlarchive ───────────────────────────────────────────────────────────
Tables 4 tables
Rows 9,705
Consistency per-table - each table read on its own, no shared instant
File 114.5 KB at Ventas.sqlarchive
Elapsed 0:02
- [dbo].[PrecioHistoria] is the history of [dbo].[Precio] and is archived as a
table of its own, rows and all, so a restore hands versioning a history that
answers FOR SYSTEM_TIME the way the source did.
# con --consistent cambia una líneawith --consistent, one line changes
Consistency snapshot - every table read from the same instantLa línea Consistency es la primera que conviene leer.
per-table quiere decir que dos tablas no tienen por qué ser del mismo instante;
si la base recibe escrituras mientras se exporta, --consistent es lo que da uno
solo. Con --consistent el modo es snapshot cuando pudo crear un
snapshot de base de datos, o snapshot isolation cuando leyó por una sola
conexión; si no puede ninguno de los dos, no exporta. Una tabla versionada arrastra su
historia como tabla aparte, y el export lo dice.
The Consistency line is the first one worth reading.
per-table means two tables need not be the same instant; if the database takes
writes while it is being exported, --consistent is what gives a single one. With
--consistent the mode is snapshot when it could create a database
snapshot, or snapshot isolation when it read through a single connection; when it
can do neither, it does not export. A system-versioned table brings its history along as a
table of its own, and the export says so.
OpcionesOptions
-s, --source <CONNECTION> | Cadena de conexión de la base a archivar.Connection string of the database to archive. |
-o, --out <FILE> | El archivo a escribir. La extensión habitual es .sqlarchive; no se exige.The archive to write. The conventional extension is .sqlarchive; it is not enforced. |
--table <GLOB> · --exclude <GLOB> | Tablas por glob esquema.tabla, repetibles. --exclude se aplica después de --table. Por defecto, todas.Tables by schema.table glob, repeatable. --exclude is applied after --table. Default: every table. |
--where <TABLE=PREDICATE> | Sólo las filas de una tabla que cumplen un predicado, como dbo.Order=Total>0. Repetible. Queda escrito en el manifiesto como rowFilter, para que un verify posterior no reporte como diferencia lo que se dejó fuera a propósito.Only the rows of one table that match a predicate, as dbo.Order=Total>0. Repeatable. Written into the manifest as that table's rowFilter, so a later verify does not report as a difference what was left out on purpose. |
--schema-only | El esquema y ninguna fila. Cada tabla queda marcada dataSkipped en el manifiesto.The schema and none of the rows. Every table is marked dataSkipped in the manifest. |
--consistent | Todas las tablas de un mismo instante: primero un snapshot de base de datos, si no una sola conexión con aislamiento SNAPSHOT. Si no hay ninguno de los dos, se niega y dice qué permiso u opción falta; no cae en silencio a tabla por tabla. El modo usado queda en el manifiesto.Every table at one point in time: a database snapshot first, else a single connection under SNAPSHOT isolation. If neither is available it refuses and says which permission or setting is missing; it does not quietly fall back to table by table. The mode used is recorded in the manifest. |
--maxdop <N> | Cuántas unidades se leen a la vez: una tabla entera es una, y cada rango de una tabla partida es otra. Por defecto, el número de procesadores con tope de 8. El snapshot de base conserva el paralelismo; el aislamiento SNAPSHOT lo pierde, porque lee por una sola conexión.How many units are read at once: a whole table is one, and each range of a split table is one more. Default: the processor count, capped at 8. A database snapshot keeps this parallelism; SNAPSHOT isolation loses it, because it reads through a single connection. |
--range-size <ROWS> | Parte una tabla grande en ficheros de más o menos tantas filas, que es lo que permite leerla en paralelo. Sólo donde hay una clave numérica o de fecha por la que partir; una tabla sin ella se lee entera.Splits a large table into range files of about this many rows, which is what lets it be read in parallel. Only where there is a numeric or date key to partition by; a table without one is read whole. |
--spool <DIR> · --resume | Dónde se guarda el export a medias (por defecto, junto a --out), y retomarlo por las tablas que faltan. La huella cubre conexión, filtros y versión de formato: retomar con otros se rechaza, en vez de mezclar dos corridas en un archivo.Where the partial export is spooled (default: beside --out), and carrying it on with the tables that are missing. The fingerprint covers connection, filters and format version: resuming with different ones is refused rather than mixing two runs into one archive. |
import
Restaura un archivo en una base que ya existe. Lo que hace depende de lo que encuentra: una base vacía recibe el esquema del archivo; una que ya tiene tablas recibe una migración, que altera donde eso conserva las filas y no borra nada.
Restores an archive into a database that already exists. What it does depends on what it finds: an empty database gets the archive's schema; one that already holds tables gets a migration, which alters where that keeps the rows and drops nothing.
Cómo se correHow to run it
# la base tiene que existir antesthe database has to exist first
sqlcmd -S . -E -Q "CREATE DATABASE VentasCopia"
# qué haría, sin escribir nadawhat it would do, writing nothing
sqlarchive import Ventas.sqlarchive --destination "Server=.;Database=VentasCopia;Integrated Security=true" --dry-run
# restaurarrestore
sqlarchive import Ventas.sqlarchive -d "Server=.;Database=VentasCopia;Integrated Security=true"import no hace CREATE DATABASE: contra una base que no está, sale con 1 y el mensaje del servidor.
The destination database has to exist. import does not run CREATE DATABASE: against a database that is not there, it exits 1 with the server's message.
Cannot open database "VentasNoExiste" requested by the login. The login failed.Base vacía: las fases del propio archivoEmpty database: the archive's own phases
Si el destino no tiene tablas, se corren las fases de esquema del archivo alrededor de
los datos: tablas desnudas, las filas, y después claves, índices y claves foráneas. Cada tabla
se publica por swap. No hace falta orden de carga, porque las claves foráneas no
existen mientras se llenan las tablas.
If the destination holds no tables, the archive's schema phases are run around the
data: bare tables, then the rows, then keys, indexes and foreign keys. Each table is published
by swap. No load order is needed, because the foreign keys are not there while the
tables are being filled.
── VentasCopia on . ────────────────────────────────────────────────────────────
Archive Ventas.sqlarchive - Ventas on PeopleWorksAI
Mode migration - the destination is diffed against the archive and altered
where that preserves rows
Schema 44 statements run
Rows 9,705
Elapsed 00:00:02
╭────────────────────────┬───────┬──────┬───────────╮
│ Table │ Rows │ How │ │
├────────────────────────┼───────┼──────┼───────────┤
│ [dbo].[Cliente] │ 1,200 │ swap │ published │
│ [dbo].[Precio] │ 3 │ swap │ published │
│ [dbo].[PrecioHistoria] │ 2 │ swap │ published │
│ [ventas].[Pedido] │ 8,500 │ swap │ published │
╰────────────────────────┴───────┴──────┴───────────╯
- The destination holds no tables, so the archive's own schema phases are run
around the data - bare tables, then the rows, then the keys, indexes and foreign
keys. That is the shape the archive was written for, and it is why a restore
needs no load order: the foreign keys are not there while the tables are being
filled.
Restored. 4 tables published, 0 tables not - each with its reason above.La línea Mode dice migration también aquí, aunque la ruta
fue la de las fases: es un error de texto de 0.1.0, corregido en 0.1.1, donde
dice schema and rows. La ruta que se tomó es la que nombra el aviso de abajo.
The Mode line says migration here too, although the route
was the phases: a wording error in 0.1.0, fixed in 0.1.1, where it reads
schema and rows. The route taken is the one the notice underneath names.
Base con tablas: una migraciónDatabase with tables: a migration
Si el destino ya tiene tablas, su esquema se compara con el del archivo y se altera
donde eso conserva las filas. No se borra nada: lo que el destino tiene y el archivo no,
se queda. Cada tabla se publica en una transacción propia: por SWITCH donde se
puede, y por DELETE e INSERT donde no —una tabla con una clave
foránea apagada, o una con columnas GENERATED ALWAYS, como una temporal—, que es lo
que muestra la columna How. Las claves foráneas del destino se apagan durante los
datos y se revalidan a la vuelta; una tabla temporal pierde su período y lo recupera en esa
misma transacción, y su historia se carga con el versionado apagado mientras dura. Donde es
INSERT, los triggers del destino se disparan.
If the destination already holds tables, its schema is diffed against the archive's
and altered where that keeps the rows. Nothing is dropped: whatever the destination has
and the archive does not stays. Each table is published in a transaction of its own: by
SWITCH where it can be, and by DELETE and INSERT where it
cannot — a table with a foreign key switched off, or one with GENERATED ALWAYS
columns, such as a temporal table — which is what the How column shows. The
destination's foreign keys are switched off for the data phase and re-validated on the way back;
a temporal table has its period taken off and put back in that same transaction, and its history
is loaded with versioning off for as long as it lasts. Where it is an INSERT, the
destination's triggers fire.
── VentasCopia on . ────────────────────────────────────────────────────────────
Schema 7 statements run
Rows 9,705
╭────────────────────────┬───────┬─────────────────────────────────┬───────────╮
│ Table │ Rows │ How │ │
├────────────────────────┼───────┼─────────────────────────────────┼───────────┤
│ [dbo].[Cliente] │ 1,200 │ insert (a foreign key of this │ published │
│ │ │ table is switched off, and a │ │
│ │ │ switch needs both sides to │ │
│ │ │ agree) │ │
│ [dbo].[Precio] │ 3 │ insert, with the period taken │ published │
│ │ │ off and put back in the same │ │
│ │ │ transaction (SQL Server refuses │ │
│ │ │ a period's own values while it │ │
│ │ │ exists) │ │
│ [dbo].[PrecioHistoria] │ 2 │ insert, as the history of [ │ published │
│ │ │ dbo].[Precio], with versioning │ │
│ │ │ off for the length of the same │ │
│ │ │ transaction │ │
…
╰────────────────────────┴───────┴─────────────────────────────────┴───────────╯
- The destination already holds 3 tables, so this is a migration: 0 object(s)
created, 1 altered, and nothing dropped. Objects the destination has and the
archive does not are left alone.Antes de correrla, --dry-run hace la misma comparación contra el
destino tal como está, dice qué publicaría, y no escribe nada:
Before running it, --dry-run makes the same comparison against the
destination as it is now, says what it would publish, and writes nothing:
── dry run - what restoring into VentasCopia on . would do ─────────────────────
Schema 8 statements to run
Rows 0
╭────────────────────────┬───────┬─────┬────────────────────╮
│ Table │ Rows │ How │ │
├────────────────────────┼───────┼─────┼────────────────────┤
│ [dbo].[Cliente] │ 1,200 │ │ would be published │
│ [dbo].[Precio] │ 3 │ │ would be published │
│ [dbo].[PrecioHistoria] │ 2 │ │ would be published │
│ [ventas].[Pedido] │ 8,500 │ │ would be published │
╰────────────────────────┴───────┴─────┴────────────────────╯
…
- 1 foreign key of the destination would be switched off for the data phase and
put back afterwards: every table here is replaced whole, and SQL Server refuses
to empty a table another key points at whichever way you empty it. They are
re-validated on the way back, which is the first moment at which validating them
means anything - the rows on both sides are the archive's.
Nothing was written. 4 tables would be published; the schema comparison above
was made against the destination as it is now.Para una copia exacta, una base vacíaFor an exact copy, an empty database
Telefono: sobrevivió al import, y un verify justo después salió con 3 nombrándola. Restaurar sobre una base viva no tira lo que otro puso ahí; por eso mismo, una copia exacta se restaura en una base vacía.
A migration leaves alone what the archive does not know about. In the test, a column Telefono had been added to the destination: it survived the import, and a verify right after exited 3 naming it. Restoring over a live database does not throw away what someone else put there; for that very reason, an exact copy is restored into an empty database.
…
3 tables match, 1 table differ
[dbo].[Cliente] the database has a column the archive does not: [Telefono]
varchar(20).
Differences found. This run did what it was asked and the two sides do not
match, so it returns 3 rather than 1, which is what a run that could not make
the comparison returns.import sale con 1.
The guard is exact, and on both sides. The rows read out of the archive and the rows that landed in staging must both match the manifest's count and hash, or that table is not published. SyncJob guesses with a row floor because it does not know how many rows there should be; here the manifest says. If any table is not published, import exits 1.
Un aviso que puede aparecer, y no hace dañoA note that may appear, and does no harm
Con varias tablas en paralelo, SQL Server a veces elige una publicación como víctima
de un deadlock, e import la vuelve a correr. Pasó en 2 de 5 imports de este archivo
de 4 tablas con el --maxdop por defecto, y en ninguno con --maxdop 1.
Una publicación es una transacción, así que el intento que perdió no dejó nada; sólo se
reintenta el error 1205, y un número acotado de veces. La causa es que SyncJob.Core 1.0.0 lee
el catálogo entero por cada tabla; el arreglo le corresponde a SyncJob.Core 1.1.
With several tables in parallel, SQL Server sometimes chooses a publication as a
deadlock victim, and import runs it again. It happened in 2 of 5 imports of this
4-table archive at the default --maxdop, and in none with --maxdop 1.
A publication is one transaction, so the attempt that lost left nothing behind; only error 1205
is rerun, and a bounded number of times. The cause is SyncJob.Core 1.0.0 reading the whole
catalog for every table; the fix belongs to SyncJob.Core 1.1.
- SQL Server chose 1 publication as a deadlock victim and each was run again, as
the server asks: [dbo].[PrecioHistoria]. A publication is one transaction, so
the attempt that lost left nothing behind. The collision is between one table's
publication reading the whole catalog - SyncJob.Core 1.0.0's staging factory and
swap each do, per table - and another's dropping its staging table.OpcionesOptions
<ARCHIVE> | El archivo a restaurar.The archive to restore. |
-d, --destination <CONNECTION> | Cadena de conexión de la base donde restaurar. No tiene que estar vacía ni ser nueva, pero tiene que existir.Connection string of the database to restore into. It does not have to be empty or new, but it has to exist. |
--dry-run | Dice qué alteraría y qué publicaría, y no escribe nada. El diff de esquema es real; el destino no se toca.Says what it would alter and what it would publish, and writes nothing. The schema diff is real; the destination is not touched. |
--schema-only | Corre las fases de esquema y se detiene. No lee ninguna fila del archivo.Runs the schema phases and stops. No rows are read out of the archive. |
--data-only | Salta el esquema y publica las filas en un destino que ya tiene la forma correcta. Se rechaza donde la forma no coincide.Skips the schema and publishes the rows into a destination that already has the right shape. Refused where the shape does not match. |
--table <GLOB> · --exclude <GLOB> | Eligen de qué tablas se publican las filas, no el esquema: las fases de esquema corren enteras, así que una tabla dejada fuera se crea igual y queda vacía. El resumen dice cuáles y por qué.They choose which tables have their rows published, not the schema: the schema phases run whole, so a table left out is still created, and left empty. The summary says which and why. |
--continue-on-error | Sigue con las demás tablas cuando una falla. Cada tabla se publica atómicamente por sí sola: las que salieron quedan publicadas y las que fallaron quedan intactas, no a medio cargar.Carries on with the remaining tables when one fails. Each table is published atomically by itself: the ones that succeeded stay published and the ones that failed are left untouched, not half loaded. |
--maxdop <N> | Cuántas tablas se preparan a la vez. Por defecto, el número de procesadores con tope de 8. --maxdop 1 evita el aviso de deadlock de arriba.How many tables are staged at once. Default: the processor count, capped at 8. --maxdop 1 avoids the deadlock note above. |
--resume · --work-dir <DIR> | Retoma una restauración interrumpida por las tablas que faltan, contra la misma huella que escribe el export. El diario vive en --work-dir (por defecto, junto al archivo) y no guarda filas, sólo qué tablas están hechas.Carries on an interrupted restore with the tables that are missing, against the same fingerprint the export writes. The journal lives in --work-dir (default: beside the archive) and holds no rows, only which tables are done. |
verify
El mismo manifiesto contesta si el archivo sigue siendo lo que dice ser, y si una base (restaurada o viva) sigue siendo lo que el archivo dice. El veredicto nombra la tabla, nunca la fila.
The same manifest answers whether the archive is still what it says it is, and whether a database, restored or live, still is what the archive says. The verdict names the table, never the row.
| PreguntaQuestion | CómoHow |
|---|---|
| ¿El archivo está íntegro?Is the archive intact? | verify Ventas.sqlarchive — los hashes de fichero del manifiesto contra las entradas. No toca ningún servidor.the file hashes in the manifest against the entries. It touches no server. |
| ¿El destino restaurado coincide?Does the restored destination match? | --against <CONNECTION> — integridad, esquema por diff, y por tabla el conteo y el hash del contenido.integrity, the schema by diff, and per table the row count and the content hash. |
| ¿Una base viva ha derivado?Has a live database drifted? | Lo mismo, apuntando a producción desde un job nocturno, con --json. Es el mismo código.The same, pointed at production from a nightly job, with --json. It is the same code. |
Cómo se correHow to run it
# el archivo contra sí mismo: no toca ningún servidorthe archive against itself: touches no server
sqlarchive verify Ventas.sqlarchive
# contra una baseagainst a database
sqlarchive verify Ventas.sqlarchive --against "Server=.;Database=VentasCopia;Integrated Security=true"
# para un job: el veredicto también en JSON; se ramifica por el código de salidafor a job: the verdict as JSON too; branch on the exit code
sqlarchive verify Ventas.sqlarchive -a "Server=.;Database=Ventas;Integrated Security=true" --json drift.jsonSin --against: el archivo contra sí mismoWithout --against: the archive against itself
── Ventas.sqlarchive ───────────────────────────────────────────────────────────
Archive Ventas.sqlarchive
Compared with nothing - the archive was checked against itself and no server
was touched
Took 0.0 s
Integrity 16 entries intact
No database was named, so this compared the archive against itself and touched
no server. That is the question an archive exists to answer: whether this file
is still what it says it is. Pass --against to compare it with a database.
No differences. Every entry hashes to what the manifest declares.Con --against: una copia recién restauradaWith --against: a freshly restored copy
Archive Ventas.sqlarchive
Compared with VentasCopia
Took 1.3 s
Integrity 16 entries intact
Schema the two describe the same objects
not compared: The name of the database. An archive of one database
restored into another with a different name is a correct restore, not
drift.
╭────────────────────────┬─────────┬───────┬─────────────╮
│ Table │ Verdict │ Rows │ Content │
├────────────────────────┼─────────┼───────┼─────────────┤
│ [dbo].[Cliente] │ matches │ 1,200 │ 8a4617bb... │
│ [dbo].[Precio] │ matches │ 3 │ 226d7abe... │
│ [dbo].[PrecioHistoria] │ matches │ 2 │ 0b72d44f... │
│ [ventas].[Pedido] │ matches │ 8,500 │ 6cac98e8... │
╰────────────────────────┴─────────┴───────┴─────────────╯
4 tables match
No differences. VentasCopia is what this archive says it is.El nombre de la base no se compara, a propósito: restaurar Ventas en
VentasCopia es una restauración correcta, no una deriva.
The database name is deliberately not compared: restoring Ventas into
VentasCopia is a correct restore, not drift.
La misma copia, después de tocarlaThe same copy, after it was touched
En la copia: un UPDATE del Email de 30 clientes, un
DELETE de 10 pedidos, y una columna Telefono agregada. Sale con
3.
On the copy: an UPDATE of 30 customers' Email, a
DELETE of 10 orders, and an added column Telefono. It exits
3.
Archive Ventas.sqlarchive
Compared with VentasCopia
Took 0.5 s
Integrity 16 entries intact
Schema
1 object different on the two sides: [dbo].[Cliente]
not compared: The name of the database. An archive of one database
restored into another with a different name is a correct restore, not
drift.
╭───────────────────────┬──────────┬───────────────┬───────────────────────────╮
│ Table │ Verdict │ Rows │ Content │
├───────────────────────┼──────────┼───────────────┼───────────────────────────┤
│ [dbo].[Cliente] │ schema │ 1,200 │ 8a4617bb... -> │
│ │ │ │ 61d73c51... │
│ [dbo].[Precio] │ matches │ 3 │ 226d7abe... │
│ [dbo].[ │ matches │ 2 │ 0b72d44f... │
│ PrecioHistoria] │ │ │ │
│ [ventas].[Pedido] │ row │ 8,500 -> │ 6cac98e8... -> │
│ │ count │ 8,490 │ 32922344... │
╰───────────────────────┴──────────┴───────────────┴───────────────────────────╯
2 tables match, 2 tables differ
[dbo].[Cliente] the database has a column the archive does not: [Telefono]
varchar(20).
1,200 rows on both sides, and the content differs.
[ventas].[Pedido] the archive declares 8,500 rows and the database holds 8,490.
Differences found. This run did what it was asked and the two sides do not
match, so it returns 3 rather than 1, which is what a run that could not make
the comparison returns.
The same verdict, as JSON, is in drift.json.1,200 rows on both sides, and the content differs es el caso que un
conteo no ve: los 30 UPDATE dejaron la misma cantidad de filas, y el hash del
contenido es lo que cambió. La lista de abajo da cada diferencia de cada tabla; la columna
Verdict muestra una.
1,200 rows on both sides, and the content differs is the case a count
cannot see: the 30 UPDATEs left the same number of rows, and the content hash is
what changed. The list underneath gives every difference of every table; the
Verdict column shows one.
Los veredictosThe verdicts
- matches
- Mismo esquema, mismas filas, mismo contenido.Same schema, same rows, same content.
- schema
- La tabla está en los dos lados, con distinta forma.Both sides have the table, shaped differently.
- row count
- Distinto número de filas.A different number of rows.
- content
- El mismo número de filas con otros valores: lo que un conteo no ve.The same number of rows holding other values: what a count cannot see.
- missing · extra
- La tabla está sólo en el archivo, o sólo en la base.The table is only in the archive, or only in the database.
- cannot say
- El manifiesto no puede responder por ella: las filas no se archivaron a propósito, o no hay hash. Ni diferencia ni coincidencia.The manifest cannot answer for it: the rows were deliberately not archived, or there is no hash. Neither a difference nor a match.
- not compared
- Dejada fuera por un glob o por
--schema-only.Left out by a glob or by--schema-only.
--json drift.json, recortado--json drift.json, trimmed
{
"archive": "Ventas.sqlarchive",
"database": "VentasCopia",
…
"tables": [
…
{
"schema": "ventas",
"name": "Pedido",
"outcome": "rowCountDiffers",
"archiveRows": 8500,
"databaseRows": 8490,
"archiveHash": "6cac98e8625768a5…",
"databaseHash": "32922344ae4b62e4…",
"differences": [
"the archive declares 8,500 rows and the database holds 8,490."
]
}
],
"matching": 2,
"differing": 2,
"unverifiable": 0,
"hasDifferences": true
}El informe legible sigue saliendo por consola. Un job ramifica por el código de
salida y abre el JSON cuando es 3.
The readable report still goes to the console. A job branches on the exit code and
opens the JSON when it is 3.
OpcionesOptions
<ARCHIVE> | El archivo a verificar: el lado de la comparación que es un fichero.The archive to verify: the side of the comparison that is a file. |
-a, --against <CONNECTION> | Compara con una base viva: el esquema por diff, y por tabla el conteo y el hash del contenido. Sin ella sólo se comprueba la integridad del archivo, y no se toca ningún servidor.Compares with a live database: the schema by diff, and per table the row count and the content hash. Without it only the archive's own integrity is checked, and no server is touched. |
--table <GLOB> · --exclude <GLOB> | Sólo las tablas que coinciden, o dejar fuera las que coinciden, como esquema.tabla. Repetibles; --exclude se aplica después de --table.Only the tables that match, or leave out the ones that match, as schema.table. Repeatable; --exclude is applied after --table. |
--schema-only | Compara el esquema y se detiene. No lee ninguna tabla de ningún lado.Compares the schema and stops. No table is read on either side. |
--json <FILE> | Escribe el veredicto también como JSON, para que lo lea un build. El informe legible sigue saliendo por consola.Writes the verdict as JSON as well, for a build to read. The readable report still goes to the console. |
--maxdop <N> | Cuántas tablas se leen a la vez. Por defecto, el número de procesadores.How many tables are read at once. Default: the processor count. |
--timeout <SECONDS> | Timeout de lectura de una tabla. 0, el valor por defecto, es sin límite: verify lee tablas enteras, y un reloj es la forma equivocada de notar una lenta.Command timeout for reading a table. 0, the default, is no limit: verify reads whole tables, and a clock is the wrong way to notice a slow one. |
inspect
Lee lo que el archivo dice de sí mismo y no descomprime los datos, así que es igual de rápido con cien gigabytes que con uno. No toca ningún servidor.
Reads what the archive says about itself and never unpacks the data, so it is as fast on a hundred gigabytes as on one. It touches no server.
Las tres formas de usarloThe three ways to run it
# ¿qué hay en este archivo?what is in this file?
sqlarchive inspect Ventas.sqlarchive
# cada entrada, comprimida y sin comprimirevery entry, packed and unpacked
sqlarchive inspect Ventas.sqlarchive --entries
# el manifiesto tal como está en el archivothe manifest exactly as the archive holds it
sqlarchive inspect Ventas.sqlarchive --json | jq .tables--json no reserializa nada: entrega los bytes del manifiesto. Es
también la forma de ver los hashes enteros, que en la tabla salen recortados a 16 de 64
caracteres.
--json re-serializes nothing: it hands over the manifest's own
bytes. It is also how to see hashes in full — the table shows 16 of their 64 characters.
Qué acepta como archivoWhat counts as an archive
*.sqlarchive | Un archivo nuestro.One of ours. |
*.zip | Uno de dbdumper, si lleva su manifest.json.One of dbdumper's, if it carries its manifest.json. |
| una carpetaa directory | Un archivo ya descomprimido, con el manifest.json dentro.An archive already unpacked, with the manifest.json in it. |
manifest.json | El manifiesto solo, que es casi todo lo que inspect imprime.The manifest on its own, which is most of what inspect prints. |
Si no es ninguna de las cuatro, dice por qué no es un archivo nuestro y por qué tampoco es uno de dbdumper, en vez de sólo negarse.
When it is none of the four, it says why it is not one of ours and why it is not one of dbdumper's, instead of just refusing.
Lo que imprimeWhat it prints
── Ventas.sqlarchive ───────────────────────────────────────────────────────────
Format sqlarchive, version 1
Written by SqlArchive 0.1.0
Created 2026-09-12 22:56:37 +00:00
Consistency per-table - each table read on its own, so two tables need not be
the same instant
Server PeopleWorksAI / Enterprise Developer Edition (64-bit) /
17.0.1000.7
Database Ventas / SQL_Latin1_General_CP1_CI_AS
File 114.5 KB on disk
Schema 1 schema
4 tables, 1 view
╭────────────────────┬───────┬─────────────────────┬───────┬───────────────────╮
│ Table │ Rows │ Row hash │ Files │ Notes │
├────────────────────┼───────┼─────────────────────┼───────┼───────────────────┤
│ [dbo].[Cliente] │ 1,200 │ 8a4617bb9f7e69d3... │ 1 │ │
│ [dbo].[Precio] │ 3 │ 226d7abe80b37431... │ 1 │ │
│ [dbo].[ │ 2 │ 0b72d44ffb077b58... │ 1 │ history of [dbo]. │
│ PrecioHistoria] │ │ │ │ [Precio] │
│ [ventas].[Pedido] │ 8,500 │ 6cac98e8625768a5... │ 1 │ │
╰────────────────────┴───────┴─────────────────────┴───────┴───────────────────╯
4 tables, 9,705 rows declared. Row hashes are shown to 16 of 64 characters;
--json prints the manifest in full.
17 entries, 708.8 KB unpacked, 112.5 KB packed.
The manifest accounts for every entry exactly once. Whether the bytes still hash
to what it says is what verify answers.Cuando el archivo es parcial —tablas con rowFilter, o archivadas sin
filas— lo dice en un recuadro antes que nada: el manifiesto lo registra, así que un
verify sabe que esas filas faltan a propósito y no las reporta como deriva, y un
import no repone lo que nunca se tomó. También dice qué columnas el formato no
lleva. Un archivo parcial que no dice que lo es hace que un verify reporte
diferencias que no son diferencias.
When the archive is partial — tables carrying a rowFilter, or archived
without their rows — it says so in a banner before anything else: the manifest records it, so a
verify knows those rows are absent on purpose and does not report them as drift,
and an import does not put back what was never taken. It also says which columns
the format does not carry. A partial archive that does not say so makes a verify
report differences that are not differences.
El archivoThe archive
Un zip. El manifiesto va al final porque lleva los hashes de todo lo demás, y eso no cuesta nada: un zip guarda su directorio al final.
A zip. The manifest goes in last because it carries the hashes of everything else, and that costs nothing: a zip keeps its directory at the end.
La formaThe shape
Ventas.sqlarchive
├── manifest.json # esquema + conteo y hash por tablaschema + count and hash per table
├── schema/010_schemas.sql # en el orden de sus prefijosin the order of their prefixes
├── schema/040_tables.sql
├── schema/070_foreignkeys.sql
├── schema/090_finalize.sql # reseed, secuencias, versionadoreseed, sequences, versioning
├── data/dbo.Customer.jsonl # una fila por líneaone row per line
├── data/dbo.Order.0000.jsonl # tabla grande, un fichero por rangobig table, one file per range
└── README.txtQue las fases sean ejecutables a mano no es decoración: es lo que hace que el archivo sirva de archivo cuando ya no exista la herramienta.
That the phases run by hand is not decoration: it is what makes the archive an archive on the day the tool is gone.
Nombres de entradaEntry names
Un identificador SQL puede llevar casi cualquier cosa. Se codifican en porcentaje los caracteres que harían el nombre ambiguo o inextraíble — el punto incluido, porque el punto es el separador.
A SQL identifier may contain almost anything. The characters that would make a name ambiguous or unextractable are percent-encoded — the dot included, because the dot is the separator.
[dbo].[My.Table] → data/dbo.My%2ETable.jsonl
[dbo.My].[Table] → data/dbo%2EMy.Table.jsonlSin escapar el punto los dos darían el mismo nombre. La codificación es inyectiva y nunca se invierte: el manifiesto lista las entradas de cada tabla.
Without escaping the dot the two would collide. The encoding is injective and is never reversed: the manifest lists each table's entries explicitly.
La igualdadEquality
La tabla de codificación de valores es normativa: es la definición de que dos filas son la misma fila. No es un detalle del exportador.
The value-encoding table is normative: it is the definition of two rows being the same row. It is not an implementation detail of the exporter.
| Tipo SQLSQL type | JSON | Por quéWhy |
|---|---|---|
bit | true / false | Un tipo de dos valores escrito como el tipo de dos valores de JSON.A two-valued type written as JSON's two-valued type. |
int, bigint | número, sin exponentenumber, no exponent | Un bigint más allá de 2⁵³ es exacto aquí; un lector propio debe parsearlo a 64 bits.A bigint past 2⁵³ is exact here; a hand-written reader should parse it as 64-bit. |
decimal, numeric | cadenastring | Vía SqlDecimal, con los 38 dígitos y la escala exacta que declara la columna: decimal(19,4) guarda uno como "1.0000".Through SqlDecimal, all 38 digits, at exactly the column's scale: a decimal(19,4) holding one is "1.0000". |
money | cadena, 4 decimalesstring, four decimals | SqlMoney.Value, nunca ToString(), que escribe un número variable de decimales.SqlMoney.Value, never ToString(), which writes a variable number of decimals. |
float / real | númeronumber | Round-trip más corto, a la precisión del tipo: un real con 0.1 es 0.1, no 0.10000000149011612.Shortest round-trip at the type's precision: a real holding 0.1 is 0.1, not 0.10000000149011612. |
datetime2 | "2026-01-15T10:00:00.003" | ISO 8601 con la T: la forma con espacio se lee según el DATEFORMAT de la sesión.ISO 8601 with the T: the space-separated form is read according to the session's DATEFORMAT. |
uniqueidentifier | forma D, minúsculasD form, lower case | Había que fijar una para que los bytes fueran estables.One had to be picked for the bytes to be stable. |
varbinary, geography | base64 | La serialización del propio servidor, leída con GetBytes: no hace falta Microsoft.SqlServer.Types ni en Linux.The server's own serialisation, read with GetBytes: no Microsoft.SqlServer.Types needed, Linux included. |
sql_variant | — | Rechazado, nombrando la columna. Su tipo base, precisión y collation tendrían que viajar por valor.Refused, naming the column. Its base type, precision and collation would have to travel per value. |
| cualquier otroanything else | — | Rechazado, nombrando el tipo. Un fallback a texto es lo que convierte un tipo desconocido en un archivo que parece correcto y no lo es.Refused, naming the type. A string fallback is what turns an unknown type into an archive that looks right and is not. |
El hash de una tablaA table's hash
La suma, módulo 2²⁵⁶, del SHA-256 de cada línea JSONL canónica — la línea tal cual está en el fichero, sin su terminador. Hex en minúsculas, 64 caracteres, sin prefijo.
The sum, modulo 2²⁵⁶, of the SHA-256 of each canonical JSONL line — the line exactly as it is in the file, without its terminator. Lowercase hex, 64 characters, no prefix.
- no depende del ordenorder-independent
- El export lee en paralelo por rangos y un verify puede leer en otro orden o con otros cortes.Export reads ranges in parallel and a verify may read in another order or with other boundaries.
- no depende del motornot the engine's opinion
- La misma fila desde 2016 y desde 2022 da los mismos bytes.
CHECKSUM_AGGhabría atado la respuesta a la versión.The same row from 2016 and from 2022 gives the same bytes.CHECKSUM_AGGwould have tied the answer to the version. - ve un UPDATEsees an UPDATE
- Que es lo que un conteo no ve. No dice qué fila cambió, y ése es el precio aceptado.Which a row count cannot. It does not say which row changed, and that is the accepted trade.
Suma, no XORThe sum, not XOR
DESIGN.md decía XOR. El XOR es una involución, así que dos
contribuciones idénticas se cancelan — y el problema no es el caso evidente que el conteo
de filas sí atrapa:
DESIGN.md said XOR. XOR is an involution, so identical
contributions cancel — and the problem is not the obvious case that the row count does
catch:
{A,A,B,B} → 0 # 4 filasrows
{C,C,D,D} → 0 # 4 filasrowsLa suma conserva todo lo que hacía falta —conmutativa, asociativa, un grupo— y no tiene involución. Una tabla vacía son 64 ceros.
The sum keeps every property that was wanted — commutative, associative, a group — and has no involution. An empty table is 64 zeros.
Lo que no viajaWhat is left out
Tres familias de columnas, y por la misma razón las tres: el servidor las escribe él y rechaza que se las escriban.
Three families of column, all for the same reason: the server writes them itself and refuses to be told what they are.
| ColumnaColumn | Por qué no viajaWhy it is left out |
|---|---|
| calculadascomputed | Se derivan de las otras. INSERT se niega a nombrarlas.Derived from the others. INSERT refuses to name one. |
rowversion | Las asigna el servidor desde un contador de una base. Una fila restaurada recibe necesariamente otra.Assigned by the server from a counter belonging to one database. A restored row necessarily gets a different one. |
| de ledgerledger | Las GENERATED ALWAYS AS TRANSACTION_ID y SEQUENCE_NUMBER de una tabla ledger. Las escribe el servidor y no admite otra cosa.A ledger table's GENERATED ALWAYS AS TRANSACTION_ID and SEQUENCE_NUMBER columns. The server writes them and accepts nothing else. |
Las columnas de período sí viajan. Una tabla versionada se archiva con su historia, y la restaurada contesta FOR SYSTEM_TIME AS OF igual que el origen en cualquier instante — también en el tramo entre el último cambio y la restauración, donde una copia ingenua no contesta nada. El período se vuelve a poner sobre las filas después de cargarlas, que es el único orden que SQL Server acepta sin perderlas.Period columns do travel. A system-versioned table is archived with its history, and the restored one answers FOR SYSTEM_TIME AS OF as the source did at every instant — including the stretch between the last change and the restore, where a naive copy answers nothing. The period is put back on the rows after they are loaded, the only order SQL Server accepts that keeps them.
verify no pudiera pasar nunca sobre una tabla que las tenga. El manifiesto las declara en omittedColumns —y inspect las muestra— para que su ausencia sea un hecho registrado y no un hueco.
Carrying them would make verify impossible to pass on a table that has one. The manifest declares them in omittedColumns — and inspect shows them — so their absence is a recorded fact rather than a gap.
Tampoco se borra lo que el archivo no conoce. Restaurar sobre una base que ya tiene tablas es una migración: lo que el destino tiene y el archivo no se queda donde está, y un verify posterior lo nombra. Para una copia exacta, restaure en una base vacía. Ver import.Nor is anything dropped that the archive does not know about. Restoring over a database that already holds tables is a migration: whatever the destination has and the archive does not stays where it is, and a later verify names it. For an exact copy, restore into an empty database. See import.
dbdumper
La forma del archivo y la tabla de codificación se adoptan de dbdumper de JeePee (MIT), con crédito y sin copiar su código.
The archive's shape and its encoding table are adopted from dbdumper by JeePee (MIT), with thanks and with no code copied.
Se leeRead
sqlarchive inspect Ventas.dbdump.zipSu manifest.json v1 se mapea a un DatabaseSnapshot, un
lado de un diff, con DbDumperManifestReader de
PeopleWorks.SqlArchive.Core. Desde la línea de comandos, en 0.1.0, sólo
inspect lo abre: verify e import lo rechazan y dicen
por qué.
Its manifest.json v1 maps onto a DatabaseSnapshot, one
side of a diff, through DbDumperManifestReader in
PeopleWorks.SqlArchive.Core. From the command line, in 0.1.0, only
inspect opens one: verify and import refuse it and say
why.
No se escribeNot written
UPDATE no se notaría. inspect lo dice en un aviso en vez de disimularlo.
Its manifest carries no per-table row hash, so nothing in it can prove its rows: an UPDATE would not show. inspect says so in a banner rather than papering over it.
Rellenar ese campo con algo calculado de los ficheros de datos haría que un archivo no verificable pareciera verificado. Por eso SqlArchive no escribe ese formato: mejorar el conteo de filas es la razón por la que existe.
Filling that field with something computed from the data files would make an unverifiable archive look verified. That is why SqlArchive does not write the format: improving on the row count is the reason it exists.
Lo que vieneWhat is coming
Dos fases por delante, y unos huecos conocidos de 0.1.0 que conviene saber antes de toparse con ellos.
Two phases ahead, and a few known gaps in 0.1.0 worth knowing before you run into them.
Fase 3 — los verbos como herramientas MCPPhase 3 — the verbs as MCP tools
Dentro de MSSQLMCPServer, para que un agente de IA pueda exportar, inspeccionar y
verificar. import también, con --dry-run por defecto.
Inside MSSQLMCPServer, so an AI agent can export, inspect and verify.
import too, with dry-run as the default.
Fase 4 — subconjuntos y enmascaradoPhase 4 — subsets and masking
Subconjuntos coherentes con las claves foráneas, y enmascarado de datos. El
manifiesto ya lleva rowFilter por tabla, así que no hará falta una versión nueva
del formato.
Subsets that stay coherent across foreign keys, and data masking. The manifest
already carries a rowFilter per table, so this will not need a new format
version.
Huecos conocidos de 0.1.0Known gaps in 0.1.0
| HuecoGap | HoyToday |
|---|---|
| Cadena de conexiónConnection string | Sólo por argumento; no hay forma de pasarla por archivo ni por variable de entorno. Mientras tanto, Integrated Security=true. Ver La secuencia habitual.Argument only; there is no way to pass it through a file or an environment variable. In the meantime, Integrated Security=true. See The usual sequence. |
| Identity restauradaRestored identity | Sigue desde el id más alto que hay en la tabla, no desde el contador que tenía el origen.It continues from the highest id in the table, not from the counter the source had. |
| Deadlock en import paraleloDeadlock in a parallel import | La publicación se vuelve a correr y no deja nada a medias; el arreglo de raíz le corresponde a SyncJob.Core 1.1. Ver import.The publication is run again and leaves nothing half done; the root fix belongs to SyncJob.Core 1.1. See import. |
ChuletaCheat sheet
Los cuatro verbos en dos tarjetas. Donde dice "..." va una cadena de conexión, como "Server=.;Database=Ventas;Integrated Security=true".
The four verbs on two cards. Where it says "...", a connection string goes, such as "Server=.;Database=Ventas;Integrated Security=true".
Archivar y mirarArchive and look
sqlarchive export -s "..." -o Ventas.sqlarchive
sqlarchive export -s "..." -o Ventas.sqlarchive --consistent
sqlarchive export -s "..." -o Esquema.sqlarchive --schema-only
sqlarchive export -s "..." -o Pedidos.sqlarchive --table "ventas.*"
sqlarchive export -s "..." -o Pedidos.sqlarchive --where "ventas.Pedido=Total>0"
sqlarchive export -s "..." -o Ventas.sqlarchive --resume
sqlarchive inspect Ventas.sqlarchive
sqlarchive inspect Ventas.sqlarchive --entries
sqlarchive inspect Ventas.sqlarchive --json
sqlarchive inspect Ventas.dbdump.zip
sqlarchive inspect ./unpacked/manifest.jsonRestaurar y comprobarRestore and check
sqlarchive import Ventas.sqlarchive -d "..." --dry-run
sqlarchive import Ventas.sqlarchive -d "..."
sqlarchive import Ventas.sqlarchive -d "..." --schema-only
sqlarchive import Ventas.sqlarchive -d "..." --data-only
sqlarchive import Ventas.sqlarchive -d "..." --maxdop 1
sqlarchive import Ventas.sqlarchive -d "..." --resume
sqlarchive verify Ventas.sqlarchive
sqlarchive verify Ventas.sqlarchive -a "..."
sqlarchive verify Ventas.sqlarchive -a "..." --json drift.json
# salida: 0 bien · 1 no pudo · 3 verify encontró diferenciasexit: 0 done · 1 could not · 3 verify found differences
sqlarchive --version
sqlarchive <verboverb> --help