> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> Guías para usar dbt con ClickHouse

# Guías

export const ClickHouseSupportedBadge = () => {
  return <div className="ClickHouseSupportedBadge">
            <div className="ClickHouseSupportedIcon">
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                    <path d="M1.30762 1.39073C1.30762 1.3103 1.37465 1.22986 1.46849 1.22986H2.64824C2.72868 1.22986 2.80912 1.29689 2.80912 1.39073V14.4886C2.80912 14.5691 2.74209 14.6495 2.64824 14.6495H1.46849C1.38805 14.6495 1.30762 14.5825 1.30762 14.4886V1.39073Z" fill="currentColor" />
                    <path d="M4.2832 1.39073C4.2832 1.3103 4.35023 1.22986 4.44408 1.22986H5.62383C5.70427 1.22986 5.7847 1.29689 5.7847 1.39073V14.4886C5.7847 14.5691 5.71767 14.6495 5.62383 14.6495H4.44408C4.36364 14.6495 4.2832 14.5825 4.2832 14.4886V1.39073Z" fill="currentColor" />
                    <path d="M7.25977 1.39073C7.25977 1.3103 7.3268 1.22986 7.42064 1.22986H8.60039C8.68083 1.22986 8.76127 1.29689 8.76127 1.39073V14.4886C8.76127 14.5691 8.69423 14.6495 8.60039 14.6495H7.42064C7.3402 14.6495 7.25977 14.5825 7.25977 14.4886V1.39073Z" fill="currentColor" />
                    <path d="M10.2354 1.39073C10.2354 1.3103 10.3024 1.22986 10.3962 1.22986H11.576C11.6564 1.22986 11.7369 1.29689 11.7369 1.39073V14.4886C11.7369 14.5691 11.6698 14.6495 11.576 14.6495H10.3962C10.3158 14.6495 10.2354 14.5825 10.2354 14.4886V1.39073Z" fill="currentColor" />
                    <path d="M13.2256 6.6057C13.2256 6.52526 13.2926 6.44482 13.3865 6.44482H14.5662C14.6466 6.44482 14.7271 6.51186 14.7271 6.6057V9.27354C14.7271 9.35398 14.6601 9.43442 14.5662 9.43442H13.3865C13.306 9.43442 13.2256 9.36739 13.2256 9.27354V6.6057Z" fill="currentColor" />
                </svg>
            </div>
            Compatible con ClickHouse
        </div>;
};

export const Image = ({img, alt, size = "lg"}) => {
  const normalizedSize = ["sm", "md", "lg"].includes(size) ? size : "lg";
  return <div className={`ch-image-${normalizedSize}`}>
      <Frame>
        <img src={img} alt={alt} />
      </Frame>
    </div>;
};

<ClickHouseSupportedBadge />

Esta sección proporciona guías para configurar dbt y el adaptador de ClickHouse, así como un ejemplo de uso de dbt con ClickHouse a partir de un conjunto de datos público de IMDB. El ejemplo abarca los siguientes pasos:

1. Crear un proyecto de dbt y configurar el adaptador de ClickHouse.
2. Definir un modelo.
3. Actualizar un modelo.
4. Crear un modelo incremental.
5. Crear un modelo snapshot.
6. Usar vistas materializadas.

Estas guías están pensadas para usarse junto con el resto de la [documentación](/docs/es/integrations/connectors/data-ingestion/etl-tools/dbt/index), las [características y configuraciones](/docs/es/integrations/connectors/data-ingestion/etl-tools/dbt/features-and-configurations) y la [referencia de materializaciones](/docs/es/integrations/connectors/data-ingestion/etl-tools/dbt/materializations).

<div id="setup">
  ## Configuración
</div>

Sigue las instrucciones de la sección [Configuración de dbt y el adaptador de ClickHouse](/docs/es/integrations/connectors/data-ingestion/etl-tools/dbt/index) para preparar tu entorno.

**Importante: Lo siguiente se ha probado con Python 3.9.**

<div id="prepare-clickhouse">
  ### Preparar ClickHouse
</div>

dbt destaca al modelar datos con muchas relaciones. A modo de ejemplo, proporcionamos un pequeño conjunto de datos de IMDb con el siguiente esquema relacional. Este conjunto de datos proviene del [repositorio de conjuntos de datos relacionales](https://relational.fit.cvut.cz/dataset/IMDb). Aunque es sencillo en comparación con los esquemas habituales que se usan con dbt, representa una muestra manejable:

<Image img="https://mintcdn.com/private-7c7dfe99/pIetLsS_hOGHqoPJ/images/integrations/data-ingestion/etl-tools/dbt/dbt_01.webp?fit=max&auto=format&n=pIetLsS_hOGHqoPJ&q=85&s=966119520059d8223dac8c84e5908794" size="lg" alt="Esquema de tablas de IMDb" width="2623" height="921" data-path="images/integrations/data-ingestion/etl-tools/dbt/dbt_01.webp" />

Usamos un subconjunto de estas tablas, como se muestra.

Cree las siguientes tablas:

```sql theme={null}
CREATE DATABASE imdb;

CREATE TABLE imdb.actors
(
    id         UInt32,
    first_name String,
    last_name  String,
    gender     FixedString(1)
) ENGINE = MergeTree ORDER BY (id, first_name, last_name, gender);

CREATE TABLE imdb.directors
(
    id         UInt32,
    first_name String,
    last_name  String
) ENGINE = MergeTree ORDER BY (id, first_name, last_name);

CREATE TABLE imdb.genres
(
    movie_id UInt32,
    genre    String
) ENGINE = MergeTree ORDER BY (movie_id, genre);

CREATE TABLE imdb.movie_directors
(
    director_id UInt32,
    movie_id    UInt64
) ENGINE = MergeTree ORDER BY (director_id, movie_id);

CREATE TABLE imdb.movies
(
    id   UInt32,
    name String,
    year UInt32,
    rank Float32 DEFAULT 0
) ENGINE = MergeTree ORDER BY (id, name, year);

CREATE TABLE imdb.roles
(
    actor_id   UInt32,
    movie_id   UInt32,
    role       String,
    created_at DateTime DEFAULT now()
) ENGINE = MergeTree ORDER BY (actor_id, movie_id);
```

<Note>
  La columna `created_at` de la tabla `roles`, cuyo valor predeterminado es `now()`. La usamos más adelante para identificar actualizaciones incrementales en nuestros modelos; consulta [Modelos incrementales](#creating-an-incremental-materialization).
</Note>

Usamos la función `s3` para leer los datos de origen desde endpoints públicos e insertarlos. Ejecuta los siguientes comandos para rellenar las tablas:

```sql theme={null}
INSERT INTO imdb.actors
SELECT *
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/imdb/imdb_ijs_actors.tsv.gz',
'TSVWithNames');

INSERT INTO imdb.directors
SELECT *
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/imdb/imdb_ijs_directors.tsv.gz',
'TSVWithNames');

INSERT INTO imdb.genres
SELECT *
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/imdb/imdb_ijs_movies_genres.tsv.gz',
'TSVWithNames');

INSERT INTO imdb.movie_directors
SELECT *
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/imdb/imdb_ijs_movies_directors.tsv.gz',
        'TSVWithNames');

INSERT INTO imdb.movies
SELECT *
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/imdb/imdb_ijs_movies.tsv.gz',
'TSVWithNames');

INSERT INTO imdb.roles(actor_id, movie_id, role)
SELECT actor_id, movie_id, role
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/imdb/imdb_ijs_roles.tsv.gz',
'TSVWithNames');
```

La ejecución de estos pasos puede variar en función de tu ancho de banda, pero cada uno debería tardar solo unos segundos en completarse. Ejecuta la siguiente consulta para obtener un resumen de cada actor, ordenado por número de apariciones en películas de mayor a menor, y confirmar que los datos se hayan cargado correctamente:

```sql theme={null}
SELECT id,
       any(actor_name)          AS name,
       uniqExact(movie_id)    AS num_movies,
       avg(rank)                AS avg_rank,
       uniqExact(genre)         AS unique_genres,
       uniqExact(director_name) AS uniq_directors,
       max(created_at)          AS updated_at
FROM (
         SELECT imdb.actors.id  AS id,
                concat(imdb.actors.first_name, ' ', imdb.actors.last_name)  AS actor_name,
                imdb.movies.id AS movie_id,
                imdb.movies.rank AS rank,
                genre,
                concat(imdb.directors.first_name, ' ', imdb.directors.last_name) AS director_name,
                created_at
         FROM imdb.actors
                  JOIN imdb.roles ON imdb.roles.actor_id = imdb.actors.id
                  LEFT OUTER JOIN imdb.movies ON imdb.movies.id = imdb.roles.movie_id
                  LEFT OUTER JOIN imdb.genres ON imdb.genres.movie_id = imdb.movies.id
                  LEFT OUTER JOIN imdb.movie_directors ON imdb.movie_directors.movie_id = imdb.movies.id
                  LEFT OUTER JOIN imdb.directors ON imdb.directors.id = imdb.movie_directors.director_id
         )
GROUP BY id
ORDER BY num_movies DESC
LIMIT 5;
```

La respuesta debería verse así:

```response theme={null}
+------+------------+----------+------------------+-------------+--------------+-------------------+
|id    |name        |num_movies|avg_rank          |unique_genres|uniq_directors|updated_at         |
+------+------------+----------+------------------+-------------+--------------+-------------------+
|45332 |Mel Blanc   |832       |6.175853582979779 |18           |84            |2022-04-26 14:01:45|
|621468|Bess Flowers|659       |5.57727638854796  |19           |293           |2022-04-26 14:01:46|
|372839|Lee Phelps  |527       |5.032976449684617 |18           |261           |2022-04-26 14:01:46|
|283127|Tom London  |525       |2.8721716524875673|17           |203           |2022-04-26 14:01:46|
|356804|Bud Osborne |515       |2.0389507108727773|15           |149           |2022-04-26 14:01:46|
+------+------------+----------+------------------+-------------+--------------+-------------------+
```

En las guías siguientes, convertiremos esta consulta en un modelo, materializándolo en ClickHouse como una vista y una tabla de dbt.

<div id="connecting-to-clickhouse">
  ## Conexión a ClickHouse
</div>

1. Cree un proyecto de dbt. En este caso, le damos el nombre de nuestra source `imdb`. Cuando se le solicite, seleccione `clickhouse` como origen de la base de datos.

   ```bash theme={null}
   clickhouse-user@clickhouse:~$ dbt init imdb

   16:52:40  Running with dbt=1.1.0
   Which database would you like to use?
   [1] clickhouse

   (Don't see the one you want? https://docs.getdbt.com/docs/available-adapters)

   Enter a number: 1
   16:53:21  No sample profile found for clickhouse.
   16:53:21
   Your new dbt project "imdb" was created!

   For more information on how to configure the profiles.yml file,
   please consult the dbt documentation here:

   https://docs.getdbt.com/docs/configure-your-profile
   ```

2. Entre en la carpeta de su proyecto con `cd`:

   ```bash theme={null}
   cd imdb
   ```

3. En este punto, necesitará el editor de texto que prefiera. En los ejemplos siguientes, usamos el popular VS Code. Al abrir el directorio IMDB, debería ver un conjunto de archivos yml y sql:

   <Image img="https://mintcdn.com/private-7c7dfe99/pIetLsS_hOGHqoPJ/images/integrations/data-ingestion/etl-tools/dbt/dbt_02.webp?fit=max&auto=format&n=pIetLsS_hOGHqoPJ&q=85&s=1e758dcd7fabd633f1250792a0d866ed" size="lg" alt="Nuevo proyecto de dbt" width="1113" height="1087" data-path="images/integrations/data-ingestion/etl-tools/dbt/dbt_02.webp" />

4. Actualice su archivo `dbt_project.yml` para especificar nuestro primer modelo, `actor_summary`, y establezca el profile en `clickhouse_imdb`.

   <Image img="https://mintcdn.com/private-7c7dfe99/pIetLsS_hOGHqoPJ/images/integrations/data-ingestion/etl-tools/dbt/dbt_03.webp?fit=max&auto=format&n=pIetLsS_hOGHqoPJ&q=85&s=7ab628f8cf11f9d6c526b0f0f1457d0a" size="lg" alt="profile de dbt" width="512" height="28" data-path="images/integrations/data-ingestion/etl-tools/dbt/dbt_03.webp" />

   <Image img="https://mintcdn.com/private-7c7dfe99/pIetLsS_hOGHqoPJ/images/integrations/data-ingestion/etl-tools/dbt/dbt_04.webp?fit=max&auto=format&n=pIetLsS_hOGHqoPJ&q=85&s=7c3d8306cc50c94bb253dbc8a316870d" size="lg" alt="profile de dbt" width="512" height="74" data-path="images/integrations/data-ingestion/etl-tools/dbt/dbt_04.webp" />

5. A continuación, debemos proporcionar a dbt los connection details de nuestra instancia de ClickHouse. Añada lo siguiente a `~/.dbt/profiles.yml`:

   ```yml theme={null}
   clickhouse_imdb:
     target: dev
     outputs:
       dev:
         type: clickhouse
         schema: imdb_dbt
         host: localhost
         port: 8123
         user: default
         password: ''
         secure: False
   ```

   Tenga en cuenta que debe modificar el usuario y la contraseña. Hay más opciones de configuración documentadas [aquí](https://github.com/silentsokolov/dbt-clickhouse#example-profile).

6. Desde el directorio IMDB, ejecute el comando `dbt debug` para confirmar si dbt puede conectarse a ClickHouse.

   ```bash theme={null}
   clickhouse-user@clickhouse:~/imdb$ dbt debug
   17:33:53  Running with dbt=1.1.0
   dbt version: 1.1.0
   python version: 3.10.1
   python path: /home/dale/.pyenv/versions/3.10.1/bin/python3.10
   os info: Linux-5.13.0-10039-tuxedo-x86_64-with-glibc2.31
   Using profiles.yml file at /home/dale/.dbt/profiles.yml
   Using dbt_project.yml file at /opt/dbt/imdb/dbt_project.yml

   Configuration:
   profiles.yml file [OK found and valid]
   dbt_project.yml file [OK found and valid]

   Required dependencies:
   - git [OK found]

   Connection:
   host: localhost
   port: 8123
   user: default
   schema: imdb_dbt
   secure: False
   verify: False
   Connection test: [OK connection ok]

   All checks passed!
   ```

   Confirme que la respuesta incluya `Connection test: [OK connection ok]`, lo que indica que la conexión se ha realizado correctamente.

<div id="creating-a-simple-view-materialization">
  ## Crear una materialización de vista simple
</div>

Cuando se usa la materialización de vista, un modelo se vuelve a crear como una vista en cada ejecución mediante una instrucción `CREATE VIEW AS` en ClickHouse. Esto no requiere almacenamiento adicional de datos, pero las consultas serán más lentas que con las materializaciones de tabla.

1. En la carpeta `imdb`, elimine el directorio `models/example`:

   ```bash theme={null}
   clickhouse-user@clickhouse:~/imdb$ rm -rf models/example
   ```

2. Cree un archivo nuevo en `actors` dentro de la carpeta `models`. Aquí creamos archivos, cada uno de los cuales representa un modelo de actores:

   ```bash theme={null}
   clickhouse-user@clickhouse:~/imdb$ mkdir models/actors
   ```

3. Crea los archivos `schema.yml` y `actor_summary.sql` en la carpeta `models/actors`.

   ```bash theme={null}
   clickhouse-user@clickhouse:~/imdb$ touch models/actors/actor_summary.sql
   clickhouse-user@clickhouse:~/imdb$ touch models/actors/schema.yml
   ```

   El archivo `schema.yml` define nuestras tablas. Posteriormente, estas estarán disponibles para usarse en macros.  Edit
   `models/actors/schema.yml` para que contenga lo siguiente:

   ```yml theme={null}
   version: 2

   sources:
   - name: imdb
     tables:
     - name: directors
     - name: actors
     - name: roles
     - name: movies
     - name: genres
     - name: movie_directors
   ```

   `actors_summary.sql` define nuestro modelo propiamente dicho. Tenga en cuenta que, en la función config, también indicamos que el modelo se materialice como una vista en ClickHouse. Nuestras tablas se referencian desde el archivo `schema.yml` mediante la función `source`; por ejemplo, `source('imdb', 'movies')` se refiere a la tabla `movies` de la base de datos `imdb`. Edite `models/actors/actors_summary.sql` para que contenga lo siguiente:

   ```sql theme={null}
   {{ config(materialized='view') }}

   with actor_summary as (
   SELECT id,
       any(actor_name) as name,
       uniqExact(movie_id)    as num_movies,
       avg(rank)                as avg_rank,
       uniqExact(genre)         as genres,
       uniqExact(director_name) as directors,
       max(created_at) as updated_at
   FROM (
           SELECT {{ source('imdb', 'actors') }}.id as id,
                   concat({{ source('imdb', 'actors') }}.first_name, ' ', {{ source('imdb', 'actors') }}.last_name) as actor_name,
                   {{ source('imdb', 'movies') }}.id as movie_id,
                   {{ source('imdb', 'movies') }}.rank as rank,
                   genre,
                   concat({{ source('imdb', 'directors') }}.first_name, ' ', {{ source('imdb', 'directors') }}.last_name) as director_name,
                   created_at
           FROM {{ source('imdb', 'actors') }}
                       JOIN {{ source('imdb', 'roles') }} ON {{ source('imdb', 'roles') }}.actor_id = {{ source('imdb', 'actors') }}.id
                       LEFT OUTER JOIN {{ source('imdb', 'movies') }} ON {{ source('imdb', 'movies') }}.id = {{ source('imdb', 'roles') }}.movie_id
                       LEFT OUTER JOIN {{ source('imdb', 'genres') }} ON {{ source('imdb', 'genres') }}.movie_id = {{ source('imdb', 'movies') }}.id
                       LEFT OUTER JOIN {{ source('imdb', 'movie_directors') }} ON {{ source('imdb', 'movie_directors') }}.movie_id = {{ source('imdb', 'movies') }}.id
                       LEFT OUTER JOIN {{ source('imdb', 'directors') }} ON {{ source('imdb', 'directors') }}.id = {{ source('imdb', 'movie_directors') }}.director_id
           )
   GROUP BY id
   )

   select *
   from actor_summary
   ```

   Fíjate en cómo incluimos la columna `updated_at` en nuestro actor\_summary final. Más adelante la usamos para las materializaciones incrementales.

4. Desde el directorio `imdb`, ejecuta el comando `dbt run`.

   ```bash theme={null}
   clickhouse-user@clickhouse:~/imdb$ dbt run
   15:05:35  Running with dbt=1.1.0
   15:05:35  Found 1 model, 0 tests, 1 snapshot, 0 analyses, 181 macros, 0 operations, 0 seed files, 6 sources, 0 exposures, 0 metrics
   15:05:35
   15:05:36  Concurrency: 1 threads (target='dev')
   15:05:36
   15:05:36  1 of 1 START view model imdb_dbt.actor_summary.................................. [RUN]
   15:05:37  1 of 1 OK created view model imdb_dbt.actor_summary............................. [OK in 1.00s]
   15:05:37
   15:05:37  Finished running 1 view model in 1.97s.
   15:05:37
   15:05:37  Completed successfully
   15:05:37
   15:05:37  Done. PASS=1 WARN=0 ERROR=0 SKIP=0 TOTAL=1
   ```

5. dbt representará el model como una vista en ClickHouse, tal como se solicitó. Ahora podemos consultar esta vista directamente. Esta vista se habrá creado en la base de datos `imdb_dbt`; esto lo determina el parámetro schema en el archivo `~/.dbt/profiles.yml`, dentro del profile `clickhouse_imdb`.

   ```sql theme={null}
   SHOW DATABASES;
   ```

   ```response theme={null}
   +------------------+
   |name              |
   +------------------+
   |INFORMATION_SCHEMA|
   |default           |
   |imdb              |
   |imdb_dbt          |  <---creado por dbt!
   |information_schema|
   |system            |
   +------------------+
   ```

   Al consultar esta vista, podemos reproducir los resultados de nuestra consulta anterior con una sintaxis más sencilla:

   ```sql theme={null}
   SELECT * FROM imdb_dbt.actor_summary ORDER BY num_movies DESC LIMIT 5;
   ```

   ```response theme={null}
   +------+------------+----------+------------------+------+---------+-------------------+
   |id    |name        |num_movies|avg_rank          |genres|directors|updated_at         |
   +------+------------+----------+------------------+------+---------+-------------------+
   |45332 |Mel Blanc   |832       |6.175853582979779 |18    |84       |2022-04-26 15:26:55|
   |621468|Bess Flowers|659       |5.57727638854796  |19    |293      |2022-04-26 15:26:57|
   |372839|Lee Phelps  |527       |5.032976449684617 |18    |261      |2022-04-26 15:26:56|
   |283127|Tom London  |525       |2.8721716524875673|17    |203      |2022-04-26 15:26:56|
   |356804|Bud Osborne |515       |2.0389507108727773|15    |149      |2022-04-26 15:26:56|
   +------+------------+----------+------------------+------+---------+-------------------+
   ```

<div id="creating-a-table-materialization">
  ## Crear una materialización como tabla
</div>

En el ejemplo anterior, nuestro modelo se materializó como una vista. Aunque esto puede ofrecer un rendimiento suficiente para algunas consultas, los `SELECT` más complejos o las consultas que se ejecutan con frecuencia pueden materializarse mejor como una tabla. Esta materialización resulta útil para los modelos que serán consultados por herramientas de BI, ya que garantiza una experiencia más ágil para los usuarios. En la práctica, esto hace que los resultados de la consulta se almacenen en una nueva tabla, con la sobrecarga de almacenamiento asociada; en efecto, se ejecuta un `INSERT TO SELECT`. Ten en cuenta que esta tabla se reconstruirá cada vez; es decir, no es incremental. Por lo tanto, los conjuntos de resultados grandes pueden dar lugar a tiempos de ejecución prolongados; consulta [dbt Limitations](/docs/es/integrations/connectors/data-ingestion/etl-tools/dbt/index#limitations).

1. Modifica el archivo `actors_summary.sql` para que el parámetro `materialized` se establezca en `table`. Observa cómo se define `ORDER BY` y que usamos el motor de tabla `MergeTree`:

   ```sql theme={null}
   {{ config(order_by='(updated_at, id, name)', engine='MergeTree()', materialized='table') }}
   ```

2. Desde el directorio `imdb`, ejecuta el comando `dbt run`. Esta ejecución puede tardar un poco más en completarse: alrededor de 10 s en la mayoría de los equipos.

   ```bash theme={null}
   clickhouse-user@clickhouse:~/imdb$ dbt run
   15:13:27  Running with dbt=1.1.0
   15:13:27  Found 1 model, 0 tests, 1 snapshot, 0 analyses, 181 macros, 0 operations, 0 seed files, 6 sources, 0 exposures, 0 metrics
   15:13:27
   15:13:28  Concurrency: 1 threads (target='dev')
   15:13:28
   15:13:28  1 of 1 START table model imdb_dbt.actor_summary................................. [RUN]
   15:13:37  1 of 1 OK created table model imdb_dbt.actor_summary............................ [OK in 9.22s]
   15:13:37
   15:13:37  Finished running 1 table model in 10.20s.
   15:13:37
   15:13:37  Completed successfully
   15:13:37
   15:13:37  Done. PASS=1 WARN=0 ERROR=0 SKIP=0 TOTAL=1
   ```

3. Confirma la creación de la tabla `imdb_dbt.actor_summary`:

   ```sql theme={null}
   SHOW CREATE TABLE imdb_dbt.actor_summary;
   ```

   Deberías ver la tabla con los tipos de datos adecuados:

   ```response theme={null}
   +----------------------------------------
   |statement
   +----------------------------------------
   |CREATE TABLE imdb_dbt.actor_summary
   |(
   |`id` UInt32,
   |`first_name` String,
   |`last_name` String,
   |`num_movies` UInt64,
   |`updated_at` DateTime
   |)
   |ENGINE = MergeTree
   |ORDER BY (id, first_name, last_name)
   +----------------------------------------
   ```

4. Confirma que los resultados de esta tabla sean coherentes con las respuestas anteriores. Observa una mejora apreciable en el tiempo de respuesta ahora que el modelo es una tabla:

   ```sql theme={null}
   SELECT * FROM imdb_dbt.actor_summary ORDER BY num_movies DESC LIMIT 5;
   ```

   ```response theme={null}
   +------+------------+----------+------------------+------+---------+-------------------+
   |id    |name        |num_movies|avg_rank          |genres|directors|updated_at         |
   +------+------------+----------+------------------+------+---------+-------------------+
   |45332 |Mel Blanc   |832       |6.175853582979779 |18    |84       |2022-04-26 15:26:55|
   |621468|Bess Flowers|659       |5.57727638854796  |19    |293      |2022-04-26 15:26:57|
   |372839|Lee Phelps  |527       |5.032976449684617 |18    |261      |2022-04-26 15:26:56|
   |283127|Tom London  |525       |2.8721716524875673|17    |203      |2022-04-26 15:26:56|
   |356804|Bud Osborne |515       |2.0389507108727773|15    |149      |2022-04-26 15:26:56|
   +------+------------+----------+------------------+------+---------+-------------------+
   ```

   No dudes en ejecutar otras consultas sobre este modelo. Por ejemplo, ¿qué actores tienen las películas mejor valoradas con más de 5 apariciones?

   ```sql theme={null}
   SELECT * FROM imdb_dbt.actor_summary WHERE num_movies > 5 ORDER BY avg_rank  DESC LIMIT 10;
   ```

<div id="creating-an-incremental-materialization">
  ## Creación de una materialización incremental
</div>

El ejemplo anterior creó una tabla para materializar el modelo. Esta tabla se reconstruirá en cada ejecución de dbt. Esto puede resultar inviable y extremadamente costoso para conjuntos de resultados grandes o transformaciones complejas. Para abordar este problema y reducir el tiempo de construcción, dbt ofrece materializaciones incrementales. Esto permite a dbt insertar o actualizar registros en una tabla desde la última ejecución, por lo que resulta adecuado para datos de tipo evento. Internamente, se crea una tabla temporal con todos los registros actualizados y, a continuación, tanto los registros no modificados como los actualizados se insertan en una nueva tabla de destino. Esto conlleva [limitaciones](/docs/es/integrations/connectors/data-ingestion/etl-tools/dbt/index#limitations) similares para conjuntos de resultados grandes a las del modelo de tabla.

Para superar estas limitaciones en conjuntos grandes, el adaptador admite el modo 'inserts\_only', en el que todas las actualizaciones se insertan en la tabla de destino sin crear una tabla temporal (más información a continuación).

Para ilustrar este ejemplo, añadiremos al actor "Clicky McClickHouse", que aparecerá en la friolera de 910 películas, lo que garantiza que haya aparecido en más filmes incluso que [Mel Blanc](https://en.wikipedia.org/wiki/Mel_Blanc).

1. Primero, modificamos nuestro modelo para que sea incremental. Este cambio requiere:

   1. **unique\_key** - Para garantizar que el adaptador pueda identificar de forma unívoca las filas, debemos proporcionar una **unique\_key**; en este caso, el campo `id` de nuestra consulta será suficiente. Esto garantiza que no tendremos filas duplicadas en nuestra tabla materializada. Para obtener más información sobre las restricciones de unicidad, consulta [aquí](https://docs.getdbt.com/docs/building-a-dbt-project/building-models/configuring-incremental-models#defining-a-uniqueness-constraint-optional).
   2. **Filtro incremental** - También debemos indicarle a dbt cómo identificar qué filas han cambiado en una ejecución incremental. Esto se consigue proporcionando una expresión delta. Normalmente, esto implica un timestamp para los datos de eventos; de ahí nuestro campo de timestamp updated\_at. Esta columna, cuyo valor predeterminado es now() cuando se insertan filas, permite identificar nuevos roles. Además, necesitamos contemplar el caso alternativo en el que se agregan nuevos actores. Usando la variable `{{this}}` para denotar la tabla materializada existente, obtenemos la expresión `where id > (select max(id) from {{ this }}) or updated_at > (select max(updated_at) from {{this}})`. La incrustamos dentro de la condición `{% if is_incremental() %}`, lo que garantiza que solo se use en ejecuciones incrementales y no cuando la tabla se construye por primera vez. Para obtener más información sobre cómo filtrar filas en modelos incrementales, consulta [este apartado de la documentación de dbt](https://docs.getdbt.com/docs/building-a-dbt-project/building-models/configuring-incremental-models#filtering-rows-on-an-incremental-run).

   Actualiza el archivo `actor_summary.sql` de la siguiente manera:

   ```sql theme={null}
   {{ config(order_by='(updated_at, id, name)', engine='MergeTree()', materialized='incremental', unique_key='id') }}
   with actor_summary as (
       SELECT id,
           any(actor_name) as name,
           uniqExact(movie_id)    as num_movies,
           avg(rank)                as avg_rank,
           uniqExact(genre)         as genres,
           uniqExact(director_name) as directors,
           max(created_at) as updated_at
       FROM (
           SELECT {{ source('imdb', 'actors') }}.id as id,
               concat({{ source('imdb', 'actors') }}.first_name, ' ', {{ source('imdb', 'actors') }}.last_name) as actor_name,
               {{ source('imdb', 'movies') }}.id as movie_id,
               {{ source('imdb', 'movies') }}.rank as rank,
               genre,
               concat({{ source('imdb', 'directors') }}.first_name, ' ', {{ source('imdb', 'directors') }}.last_name) as director_name,
               created_at
       FROM {{ source('imdb', 'actors') }}
           JOIN {{ source('imdb', 'roles') }} ON {{ source('imdb', 'roles') }}.actor_id = {{ source('imdb', 'actors') }}.id
           LEFT OUTER JOIN {{ source('imdb', 'movies') }} ON {{ source('imdb', 'movies') }}.id = {{ source('imdb', 'roles') }}.movie_id
           LEFT OUTER JOIN {{ source('imdb', 'genres') }} ON {{ source('imdb', 'genres') }}.movie_id = {{ source('imdb', 'movies') }}.id
           LEFT OUTER JOIN {{ source('imdb', 'movie_directors') }} ON {{ source('imdb', 'movie_directors') }}.movie_id = {{ source('imdb', 'movies') }}.id
           LEFT OUTER JOIN {{ source('imdb', 'directors') }} ON {{ source('imdb', 'directors') }}.id = {{ source('imdb', 'movie_directors') }}.director_id
       )
       GROUP BY id
   )
   select *
   from actor_summary

   {% if is_incremental() %}

   -- este filtro solo se aplicará en una ejecución incremental
   where id > (select max(id) from {{ this }}) or updated_at > (select max(updated_at) from {{this}})

   {% endif %}
   ```

   Ten en cuenta que nuestro modelo solo responderá a las actualizaciones y adiciones en las tablas `roles` y `actors`. Para responder a todas las tablas, se recomienda a los usuarios dividir este modelo en varios submodelos, cada uno con sus propios criterios incrementales. A su vez, estos modelos pueden referenciarse y conectarse entre sí. Para obtener más información sobre las referencias cruzadas entre modelos, consulta [aquí](https://docs.getdbt.com/reference/dbt-jinja-functions/ref).

2. Ejecuta `dbt run` y confirma los resultados de la tabla generada:

   ```response theme={null}
   clickhouse-user@clickhouse:~/imdb$  dbt run
   15:33:34  Running with dbt=1.1.0
   15:33:34  Found 1 model, 0 tests, 1 snapshot, 0 analyses, 181 macros, 0 operations, 0 seed files, 6 sources, 0 exposures, 0 metrics
   15:33:34
   15:33:35  Concurrency: 1 threads (target='dev')
   15:33:35
   15:33:35  1 of 1 START incremental model imdb_dbt.actor_summary........................... [RUN]
   15:33:41  1 of 1 OK created incremental model imdb_dbt.actor_summary...................... [OK in 6.33s]
   15:33:41
   15:33:41  Finished running 1 incremental model in 7.30s.
   15:33:41
   15:33:41  Completed successfully
   15:33:41
   15:33:41  Done. PASS=1 WARN=0 ERROR=0 SKIP=0 TOTAL=1
   ```

   ```sql theme={null}
   SELECT * FROM imdb_dbt.actor_summary ORDER BY num_movies DESC LIMIT 5;
   ```

   ```response theme={null}
   +------+------------+----------+------------------+------+---------+-------------------+
   |id    |name        |num_movies|avg_rank          |genres|directors|updated_at         |
   +------+------------+----------+------------------+------+---------+-------------------+
   |45332 |Mel Blanc   |832       |6.175853582979779 |18    |84       |2022-04-26 15:26:55|
   |621468|Bess Flowers|659       |5.57727638854796  |19    |293      |2022-04-26 15:26:57|
   |372839|Lee Phelps  |527       |5.032976449684617 |18    |261      |2022-04-26 15:26:56|
   |283127|Tom London  |525       |2.8721716524875673|17    |203      |2022-04-26 15:26:56|
   |356804|Bud Osborne |515       |2.0389507108727773|15    |149      |2022-04-26 15:26:56|
   +------+------------+----------+------------------+------+---------+-------------------+
   ```

3. Ahora vamos a agregar datos a nuestro modelo para ilustrar una actualización incremental. Añade el actor "Clicky McClickHouse" a la tabla `actors`:

   ```sql theme={null}
   INSERT INTO imdb.actors VALUES (845466, 'Clicky', 'McClickHouse', 'M');
   ```

4. Hagamos que "Clicky" protagonice 910 películas al azar:

   ```sql theme={null}
   INSERT INTO imdb.roles
   SELECT now() as created_at, 845466 as actor_id, id as movie_id, 'Himself' as role
   FROM imdb.movies
   LIMIT 910 OFFSET 10000;
   ```

5. Confirma que ahora sí es el actor con más apariciones consultando directamente la tabla de origen subyacente, sin pasar por ningún modelo de dbt:

   ```sql theme={null}
   SELECT id,
       any(actor_name)          as name,
       uniqExact(movie_id)    as num_movies,
       avg(rank)                as avg_rank,
       uniqExact(genre)         as unique_genres,
       uniqExact(director_name) as uniq_directors,
       max(created_at)          as updated_at
   FROM (
           SELECT imdb.actors.id                                                   as id,
                   concat(imdb.actors.first_name, ' ', imdb.actors.last_name)       as actor_name,
                   imdb.movies.id as movie_id,
                   imdb.movies.rank                                                 as rank,
                   genre,
                   concat(imdb.directors.first_name, ' ', imdb.directors.last_name) as director_name,
                   created_at
           FROM imdb.actors
                   JOIN imdb.roles ON imdb.roles.actor_id = imdb.actors.id
                   LEFT OUTER JOIN imdb.movies ON imdb.movies.id = imdb.roles.movie_id
                   LEFT OUTER JOIN imdb.genres ON imdb.genres.movie_id = imdb.movies.id
                   LEFT OUTER JOIN imdb.movie_directors ON imdb.movie_directors.movie_id = imdb.movies.id
                   LEFT OUTER JOIN imdb.directors ON imdb.directors.id = imdb.movie_directors.director_id
           )
   GROUP BY id
   ORDER BY num_movies DESC
   LIMIT 2;
   ```

   ```response theme={null}
   +------+-------------------+----------+------------------+------+---------+-------------------+
   |id    |name               |num_movies|avg_rank          |genres|directors|updated_at         |
   +------+-------------------+----------+------------------+------+---------+-------------------+
   |845466|Clicky McClickHouse|910       |1.4687938697032283|21    |662      |2022-04-26 16:20:36|
   |45332 |Mel Blanc          |909       |5.7884792542982515|19    |148      |2022-04-26 16:17:42|
   +------+-------------------+----------+------------------+------+---------+-------------------+
   ```

6. Ejecuta un `dbt run` y confirma que nuestro modelo se haya actualizado y coincida con los resultados anteriores:

   ```response theme={null}
   clickhouse-user@clickhouse:~/imdb$  dbt run
   16:12:16  Running with dbt=1.1.0
   16:12:16  Found 1 model, 0 tests, 1 snapshot, 0 analyses, 181 macros, 0 operations, 0 seed files, 6 sources, 0 exposures, 0 metrics
   16:12:16
   16:12:17  Concurrency: 1 threads (target='dev')
   16:12:17
   16:12:17  1 of 1 START incremental model imdb_dbt.actor_summary........................... [RUN]
   16:12:24  1 of 1 OK created incremental model imdb_dbt.actor_summary...................... [OK in 6.82s]
   16:12:24
   16:12:24  Finished running 1 incremental model in 7.79s.
   16:12:24
   16:12:24  Completed successfully
   16:12:24
   16:12:24  Done. PASS=1 WARN=0 ERROR=0 SKIP=0 TOTAL=1
   ```

   ```sql theme={null}
   SELECT * FROM imdb_dbt.actor_summary ORDER BY num_movies DESC LIMIT 2;
   ```

   ```response theme={null}
   +------+-------------------+----------+------------------+------+---------+-------------------+
   |id    |name               |num_movies|avg_rank          |genres|directors|updated_at         |
   +------+-------------------+----------+------------------+------+---------+-------------------+
   |845466|Clicky McClickHouse|910       |1.4687938697032283|21    |662      |2022-04-26 16:20:36|
   |45332 |Mel Blanc          |909       |5.7884792542982515|19    |148      |2022-04-26 16:17:42|
   +------+-------------------+----------+------------------+------+---------+-------------------+
   ```

<div id="internals">
  ### Aspectos internos
</div>

Podemos identificar las sentencias ejecutadas para realizar la actualización incremental anterior consultando el registro de consultas de ClickHouse.

```sql theme={null}
SELECT event_time, query  FROM system.query_log WHERE type='QueryStart' AND query LIKE '%dbt%'
AND event_time > subtractMinutes(now(), 15) ORDER BY event_time LIMIT 100;
```

Ajuste la consulta anterior al período de ejecución. Dejamos al usuario la inspección de los resultados, pero destacamos la estrategia general que utiliza el adaptador para realizar actualizaciones incrementales:

1. El adaptador crea una tabla temporal `actor_sumary__dbt_tmp`. Las filas que han cambiado se envían a esta tabla.
2. Se crea una nueva tabla, `actor_summary_new,`. A continuación, las filas de la tabla anterior se envían de la antigua a la nueva, con una comprobación para asegurarse de que los identificadores de fila no existan en la tabla temporal. Esto resuelve eficazmente las actualizaciones y los duplicados.
3. Los resultados de la tabla temporal se envían a la nueva tabla `actor_summary`:
4. Por último, la nueva tabla se intercambia de forma atómica con la versión anterior mediante una instrucción `EXCHANGE TABLES`. A su vez, se eliminan la tabla anterior y la temporal.

Esto se muestra a continuación:

<Image img="https://mintcdn.com/private-7c7dfe99/pIetLsS_hOGHqoPJ/images/integrations/data-ingestion/etl-tools/dbt/dbt_05.webp?fit=max&auto=format&n=pIetLsS_hOGHqoPJ&q=85&s=21a9392d8a567b64531960cfc80b3de6" size="lg" alt="actualizaciones incrementales dbt" width="1432" height="850" data-path="images/integrations/data-ingestion/etl-tools/dbt/dbt_05.webp" />

Esta estrategia puede presentar dificultades en modelos muy grandes. Para obtener más información, consulte [Limitaciones](/docs/es/integrations/connectors/data-ingestion/etl-tools/dbt/index#limitations).

<div id="append-strategy-inserts-only-mode">
  ### Estrategia append (modo de solo inserciones)
</div>

Para superar las limitaciones de los conjuntos de datos grandes en los modelos incrementales, el adaptador usa el parámetro de configuración de dbt `incremental_strategy`. Este puede establecerse con el valor `append`. Cuando se configura así, las filas actualizadas se insertan directamente en la tabla de destino (también conocida como `imdb_dbt.actor_summary`) y no se crea ninguna tabla temporal.
Nota: El modo append-only requiere que tus datos sean inmutables o que los duplicados sean aceptables. Si quieres un modelo de tabla incremental que admita filas modificadas, ¡no uses este modo!

Para ilustrar este modo, agregaremos otro actor nuevo y volveremos a ejecutar `dbt run` con `incremental_strategy='append'`.

1. Configura el modo append-only en actor\_summary.sql:

   ```sql theme={null}
   {{ config(order_by='(updated_at, id, name)', engine='MergeTree()', materialized='incremental', unique_key='id', incremental_strategy='append') }}
   ```

2. Añadamos otro actor famoso: Danny DeBito

   ```sql theme={null}
   INSERT INTO imdb.actors VALUES (845467, 'Danny', 'DeBito', 'M');
   ```

3. Hagamos que Danny aparezca en 920 películas aleatorias.

   ```sql theme={null}
   INSERT INTO imdb.roles
   SELECT now() as created_at, 845467 as actor_id, id as movie_id, 'Himself' as role
   FROM imdb.movies
   LIMIT 920 OFFSET 10000;
   ```

4. Ejecuta `dbt run` y confirma que Danny se añadió a la tabla actor-summary

   ```response theme={null}
   clickhouse-user@clickhouse:~/imdb$ dbt run
   16:12:16  Running with dbt=1.1.0
   16:12:16  Found 1 model, 0 tests, 1 snapshot, 0 analyses, 186 macros, 0 operations, 0 seed files, 6 sources, 0 exposures, 0 metrics
   16:12:16
   16:12:17  Concurrency: 1 threads (target='dev')
   16:12:17
   16:12:17  1 of 1 START incremental model imdb_dbt.actor_summary........................... [RUN]
   16:12:24  1 of 1 OK created incremental model imdb_dbt.actor_summary...................... [OK in 0.17s]
   16:12:24
   16:12:24  Finished running 1 incremental model in 0.19s.
   16:12:24
   16:12:24  Completed successfully
   16:12:24
   16:12:24  Done. PASS=1 WARN=0 ERROR=0 SKIP=0 TOTAL=1
   ```

   ```sql theme={null}
   SELECT * FROM imdb_dbt.actor_summary ORDER BY num_movies DESC LIMIT 3;
   ```

   ```response theme={null}
   +------+-------------------+----------+------------------+------+---------+-------------------+
   |id    |name               |num_movies|avg_rank          |genres|directors|updated_at         |
   +------+-------------------+----------+------------------+------+---------+-------------------+
   |845467|Danny DeBito       |920       |1.4768987303293204|21    |670      |2022-04-26 16:22:06|
   |845466|Clicky McClickHouse|910       |1.4687938697032283|21    |662      |2022-04-26 16:20:36|
   |45332 |Mel Blanc          |909       |5.7884792542982515|19    |148      |2022-04-26 16:17:42|
   +------+-------------------+----------+------------------+------+---------+-------------------+
   ```

Observa lo mucho más rápida que fue esa ejecución incremental en comparación con la inserción de "Clicky".

Si vuelves a revisar la tabla query\_log, verás las diferencias entre las 2 ejecuciones incrementales:

```sql theme={null}
INSERT INTO imdb_dbt.actor_summary ("id", "name", "num_movies", "avg_rank", "genres", "directors", "updated_at")
WITH actor_summary AS (
   SELECT id,
      any(actor_name) AS name,
      uniqExact(movie_id)    AS num_movies,
      avg(rank)                AS avg_rank,
      uniqExact(genre)         AS genres,
      uniqExact(director_name) AS directors,
      max(created_at) AS updated_at
   FROM (
      SELECT imdb.actors.id AS id,
         concat(imdb.actors.first_name, ' ', imdb.actors.last_name) AS actor_name,
         imdb.movies.id AS movie_id,
         imdb.movies.rank AS rank,
         genre,
         concat(imdb.directors.first_name, ' ', imdb.directors.last_name) AS director_name,
         created_at
      FROM imdb.actors
         JOIN imdb.roles ON imdb.roles.actor_id = imdb.actors.id
         LEFT OUTER JOIN imdb.movies ON imdb.movies.id = imdb.roles.movie_id
         LEFT OUTER JOIN imdb.genres ON imdb.genres.movie_id = imdb.movies.id
         LEFT OUTER JOIN imdb.movie_directors ON imdb.movie_directors.movie_id = imdb.movies.id
         LEFT OUTER JOIN imdb.directors ON imdb.directors.id = imdb.movie_directors.director_id
   )
   GROUP BY id
)

SELECT *
FROM actor_summary
-- este filtro solo se aplicará en una ejecución incremental
WHERE id > (SELECT max(id) FROM imdb_dbt.actor_summary) OR updated_at > (SELECT max(updated_at) FROM imdb_dbt.actor_summary)
```

En esta ejecución, solo se añaden directamente las filas nuevas a la tabla `imdb_dbt.actor_summary` y no se crea ninguna tabla.

<div id="deleteinsert-mode-experimental">
  ### Modo de eliminación e inserción (experimental)
</div>

Históricamente, ClickHouse solo ha ofrecido un soporte limitado para las actualizaciones y eliminaciones, en forma de [mutaciones](/docs/es/reference/statements/alter/index). Estas pueden consumir muchísimas operaciones de E/S y, por lo general, conviene evitarlas.

ClickHouse 22.8 introdujo las [eliminaciones ligeras](/docs/es/reference/statements/delete) y ClickHouse 25.7 introdujo las [actualizaciones ligeras](/docs/es/reference/statements/update). Con la introducción de estas funciones, los cambios realizados mediante consultas de actualización individuales, incluso cuando se materializan de forma asíncrona, se reflejan al instante desde la perspectiva del usuario.

Este modo puede configurarse para un modelo mediante el parámetro `incremental_strategy`, es decir.

```sql theme={null}
{{ config(order_by='(updated_at, id, name)', engine='MergeTree()', materialized='incremental', unique_key='id', incremental_strategy='delete+insert') }}
```

Esta estrategia opera directamente sobre la tabla del modelo de destino, por lo que, si se produce algún problema durante la operación, es probable que los datos del modelo incremental queden en un estado no válido; no hay actualización atómica.

En resumen, este enfoque:

1. El adaptador crea una tabla temporal `actor_sumary__dbt_tmp`. Las filas que han cambiado se envían a esta tabla.
2. Se ejecuta un `DELETE` en la tabla actual `actor_summary`. Las filas se eliminan por id a partir de `actor_sumary__dbt_tmp`
3. Las filas de `actor_sumary__dbt_tmp` se insertan en `actor_summary` mediante un `INSERT INTO actor_summary SELECT * FROM actor_sumary__dbt_tmp`.

Este proceso se muestra a continuación:

<Image img="https://mintcdn.com/private-7c7dfe99/pIetLsS_hOGHqoPJ/images/integrations/data-ingestion/etl-tools/dbt/dbt_06.webp?fit=max&auto=format&n=pIetLsS_hOGHqoPJ&q=85&s=1544005a262e6b9249b99fc38e0138e8" size="lg" alt="incremental con eliminación ligera" width="1345" height="528" data-path="images/integrations/data-ingestion/etl-tools/dbt/dbt_06.webp" />

<div id="insert_overwrite-mode-experimental">
  ### modo `insert_overwrite` (experimental)
</div>

Realiza los siguientes pasos:

1. Crea una tabla de staging (temporal) con la misma estructura que la relación del modelo incremental: `CREATE TABLE {staging} AS {target}`.
2. Inserta solo los registros nuevos (producidos por SELECT) en la tabla de staging.
3. Reemplaza solo las particiones nuevas (presentes en la tabla de staging) en la tabla de destino.

<br />

Este enfoque tiene las siguientes ventajas:

* Es más rápido que la estrategia predeterminada porque no copia toda la tabla.
* Es más seguro que otras estrategias porque no modifica la tabla original hasta que la operación INSERT se completa correctamente: en caso de fallo intermedio, la tabla original no se modifica.
* Implementa la práctica recomendada en ingeniería de datos de la "inmutabilidad de las particiones", lo que simplifica el procesamiento de datos incremental y en paralelo, los rollbacks, etc.

<Image img="https://mintcdn.com/private-7c7dfe99/pIetLsS_hOGHqoPJ/images/integrations/data-ingestion/etl-tools/dbt/dbt_07.webp?fit=max&auto=format&n=pIetLsS_hOGHqoPJ&q=85&s=0486243c561a2ad6c335baec32014a65" size="lg" alt="sobrescritura incremental insert" width="7084" height="2327" data-path="images/integrations/data-ingestion/etl-tools/dbt/dbt_07.webp" />

<div id="creating-a-snapshot">
  ## Crear un snapshot
</div>

Los snapshots de dbt permiten mantener un registro de los cambios en un modelo mutable a lo largo del tiempo. Esto, a su vez, permite realizar consultas en un momento determinado sobre los modelos, de modo que los analistas puedan "retroceder en el tiempo" para ver el estado anterior de un modelo. Esto se logra mediante [dimensiones de cambio lento de tipo 2](https://en.wikipedia.org/wiki/Slowly_changing_dimension#Type_2:_add_new_row), donde las columnas de fecha de inicio y fin registran cuándo una fila era válida. Esta funcionalidad es compatible con el adaptador de ClickHouse y se muestra a continuación.

Este ejemplo asume que has completado [Creating an Incremental Table Model](#creating-an-incremental-materialization). Asegúrate de que tu `actor&#95;summary.sql` no establezca `inserts&#95;only=True`. Tu `models/actor&#95;summary.sql` debería verse así:

```sql theme={null}
   {{ config(order_by='(updated_at, id, name)', engine='MergeTree()', materialized='incremental', unique_key='id') }}

   with actor_summary as (
       SELECT id,
           any(actor_name) as name,
           uniqExact(movie_id)    as num_movies,
           avg(rank)                as avg_rank,
           uniqExact(genre)         as genres,
           uniqExact(director_name) as directors,
           max(created_at) as updated_at
       FROM (
           SELECT {{ source('imdb', 'actors') }}.id as id,
               concat({{ source('imdb', 'actors') }}.first_name, ' ', {{ source('imdb', 'actors') }}.last_name) as actor_name,
               {{ source('imdb', 'movies') }}.id as movie_id,
               {{ source('imdb', 'movies') }}.rank as rank,
               genre,
               concat({{ source('imdb', 'directors') }}.first_name, ' ', {{ source('imdb', 'directors') }}.last_name) as director_name,
               created_at
       FROM {{ source('imdb', 'actors') }}
           JOIN {{ source('imdb', 'roles') }} ON {{ source('imdb', 'roles') }}.actor_id = {{ source('imdb', 'actors') }}.id
           LEFT OUTER JOIN {{ source('imdb', 'movies') }} ON {{ source('imdb', 'movies') }}.id = {{ source('imdb', 'roles') }}.movie_id
           LEFT OUTER JOIN {{ source('imdb', 'genres') }} ON {{ source('imdb', 'genres') }}.movie_id = {{ source('imdb', 'movies') }}.id
           LEFT OUTER JOIN {{ source('imdb', 'movie_directors') }} ON {{ source('imdb', 'movie_directors') }}.movie_id = {{ source('imdb', 'movies') }}.id
           LEFT OUTER JOIN {{ source('imdb', 'directors') }} ON {{ source('imdb', 'directors') }}.id = {{ source('imdb', 'movie_directors') }}.director_id
       )
       GROUP BY id
   )
   select *
   from actor_summary

   {% if is_incremental() %}

   -- este filtro solo se aplicará en una ejecución incremental
   where id > (select max(id) from {{ this }}) or updated_at > (select max(updated_at) from {{this}})

   {% endif %}
```

1. Cree un archivo `actor_summary` en el directorio `snapshots`.

   ```bash theme={null}
    touch snapshots/actor_summary.sql
   ```

2. Actualice el contenido del archivo actor\_summary.sql con lo siguiente:
   ```sql theme={null}
   {% snapshot actor_summary_snapshot %}

   {{
   config(
   target_schema='snapshots',
   unique_key='id',
   strategy='timestamp',
   updated_at='updated_at',
   )
   }}

   select * from {{ref('actor_summary')}}

   {% endsnapshot %}
   ```

Algunas observaciones sobre este contenido:

* La consulta `select` define los resultados de los que desea capturar snapshots a lo largo del tiempo. La función ref se usa para hacer referencia al modelo actor\_summary que creamos anteriormente.
* Necesitamos una columna `timestamp` para indicar los cambios en los registros. Nuestra columna updated\_at (consulte [Creación de un modelo de tabla incremental](#creating-an-incremental-materialization)) puede usarse aquí. El parámetro strategy indica que usamos un `timestamp` para señalar las actualizaciones, mientras que el parámetro updated\_at especifica la columna que se debe utilizar. Si esto no está presente en su modelo, también puede usar la [estrategia check](https://docs.getdbt.com/docs/building-a-dbt-project/snapshots#check-strategy). Esto es mucho menos eficiente y requiere que el usuario especifique una lista de columnas para comparar. dbt compara los valores actuales e históricos de estas columnas y registra cualquier cambio (o no hace nada si son idénticos).

3. Ejecuta el comando `dbt snapshot`.

   ```response theme={null}
   clickhouse-user@clickhouse:~/imdb$ dbt snapshot
   13:26:23  Running with dbt=1.1.0
   13:26:23  Found 1 model, 0 tests, 1 snapshot, 0 analyses, 181 macros, 0 operations, 0 seed files, 3 sources, 0 exposures, 0 metrics
   13:26:23
   13:26:25  Concurrency: 1 threads (target='dev')
   13:26:25
   13:26:25  1 of 1 START snapshot snapshots.actor_summary_snapshot...................... [RUN]
   13:26:25  1 of 1 OK snapshotted snapshots.actor_summary_snapshot...................... [OK in 0.79s]
   13:26:25
   13:26:25  Finished running 1 snapshot in 2.11s.
   13:26:25
   13:26:25  Completed successfully
   13:26:25
   13:26:25  Done. PASS=1 WARN=0 ERROR=0 SKIP=0 TOTAL=1
   ```

Observa que se ha creado la tabla actor\_summary\_snapshot en la base de datos snapshots (determinada por el parámetro target\_schema).

4. Al observar estos datos, verá cómo dbt ha incluido las columnas dbt\_valid\_from y dbt\_valid\_to. La última tiene valores nulos. Las ejecuciones posteriores la actualizarán.

   ```sql theme={null}
   SELECT id, name, num_movies, dbt_valid_from, dbt_valid_to FROM snapshots.actor_summary_snapshot ORDER BY num_movies DESC LIMIT 5;
   ```

   ```response theme={null}
   +------+----------+------------+----------+-------------------+------------+
   |id    |first_name|last_name   |num_movies|dbt_valid_from     |dbt_valid_to|
   +------+----------+------------+----------+-------------------+------------+
   |845467|Danny     |DeBito      |920       |2022-05-25 19:33:32|NULL        |
   |845466|Clicky    |McClickHouse|910       |2022-05-25 19:32:34|NULL        |
   |45332 |Mel       |Blanc       |909       |2022-05-25 19:31:47|NULL        |
   |621468|Bess      |Flowers     |672       |2022-05-25 19:31:47|NULL        |
   |283127|Tom       |London      |549       |2022-05-25 19:31:47|NULL        |
   +------+----------+------------+----------+-------------------+------------+
   ```

5. Haz que nuestro actor favorito, Clicky McClickHouse, aparezca en otras 10 películas.

   ```sql theme={null}
   INSERT INTO imdb.roles
   SELECT now() as created_at, 845466 as actor_id, rand(number) % 412320 as movie_id, 'Himself' as role
   FROM system.numbers
   LIMIT 10;
   ```

6. Vuelve a ejecutar el comando `dbt run` desde el directorio `imdb`. Esto actualizará el modelo incremental. Una vez completado, ejecuta `dbt snapshot` para capturar los cambios.

   ```response theme={null}
   clickhouse-user@clickhouse:~/imdb$ dbt run
   13:46:14  Running with dbt=1.1.0
   13:46:14  Found 1 model, 0 tests, 1 snapshot, 0 analyses, 181 macros, 0 operations, 0 seed files, 3 sources, 0 exposures, 0 metrics
   13:46:14
   13:46:15  Concurrency: 1 threads (target='dev')
   13:46:15
   13:46:15  1 of 1 START incremental model imdb_dbt.actor_summary....................... [RUN]
   13:46:18  1 of 1 OK created incremental model imdb_dbt.actor_summary.................. [OK in 2.76s]
   13:46:18
   13:46:18  Finished running 1 incremental model in 3.73s.
   13:46:18
   13:46:18  Completed successfully
   13:46:18
   13:46:18  Done. PASS=1 WARN=0 ERROR=0 SKIP=0 TOTAL=1

   clickhouse-user@clickhouse:~/imdb$ dbt snapshot
   13:46:26  Running with dbt=1.1.0
   13:46:26  Found 1 model, 0 tests, 1 snapshot, 0 analyses, 181 macros, 0 operations, 0 seed files, 3 sources, 0 exposures, 0 metrics
   13:46:26
   13:46:27  Concurrency: 1 threads (target='dev')
   13:46:27
   13:46:27  1 of 1 START snapshot snapshots.actor_summary_snapshot...................... [RUN]
   13:46:31  1 of 1 OK snapshotted snapshots.actor_summary_snapshot...................... [OK in 4.05s]
   13:46:31
   13:46:31  Finished running 1 snapshot in 5.02s.
   13:46:31
   13:46:31  Completed successfully
   13:46:31
   13:46:31  Done. PASS=1 WARN=0 ERROR=0 SKIP=0 TOTAL=1
   ```

7. Si ahora consultamos nuestra instantánea, observa que tenemos 2 filas para Clicky McClickHouse. Nuestra entrada anterior ahora tiene un valor en dbt\_valid\_to. El nuevo valor se registra con ese mismo valor en la columna dbt\_valid\_from y con un valor dbt\_valid\_to de null. Si hubiera filas nuevas, estas también se añadirían a la instantánea.

   ```sql theme={null}
   SELECT id, name, num_movies, dbt_valid_from, dbt_valid_to FROM snapshots.actor_summary_snapshot ORDER BY num_movies DESC LIMIT 5;
   ```

   ```response theme={null}
   +------+----------+------------+----------+-------------------+-------------------+
   |id    |first_name|last_name   |num_movies|dbt_valid_from     |dbt_valid_to       |
   +------+----------+------------+----------+-------------------+-------------------+
   |845467|Danny     |DeBito      |920       |2022-05-25 19:33:32|NULL               |
   |845466|Clicky    |McClickHouse|920       |2022-05-25 19:34:37|NULL               |
   |845466|Clicky    |McClickHouse|910       |2022-05-25 19:32:34|2022-05-25 19:34:37|
   |45332 |Mel       |Blanc       |909       |2022-05-25 19:31:47|NULL               |
   |621468|Bess      |Flowers     |672       |2022-05-25 19:31:47|NULL               |
   +------+----------+------------+----------+-------------------+-------------------+
   ```

Para obtener más información sobre los snapshots de dbt, consulte [aquí](https://docs.getdbt.com/docs/building-a-dbt-project/snapshots).

<div id="using-seeds">
  ## Uso de seeds
</div>

dbt permite cargar datos desde archivos CSV. Esta capacidad no es adecuada para cargar exportaciones grandes de una base de datos; está pensada más bien para archivos pequeños que suelen usarse para tablas de códigos y [diccionarios](/docs/es/concepts/features/dictionaries/index), por ejemplo, para asociar códigos de país con nombres de países. Como ejemplo sencillo, generaremos y luego cargaremos una lista de códigos de género mediante la funcionalidad de seed.

1. Generamos una lista de códigos de género a partir de nuestro conjunto de datos existente. Desde el directorio de dbt, use `clickhouse-client` para crear el archivo `seeds/genre_codes.csv`:

   ```bash theme={null}
   clickhouse-user@clickhouse:~/imdb$ clickhouse-client --password <password> --query
   "SELECT genre, ucase(substring(genre, 1, 3)) as code FROM imdb.genres GROUP BY genre
   LIMIT 100 FORMAT CSVWithNames" > seeds/genre_codes.csv
   ```

2. Ejecute el comando `dbt seed`. Esto creará una nueva tabla `genre_codes` en nuestra base de datos `imdb_dbt` (como se define en la configuración del esquema) con las filas de nuestro archivo CSV.

   ```bash theme={null}
   clickhouse-user@clickhouse:~/imdb$ dbt seed
   17:03:23  Running with dbt=1.1.0
   17:03:23  Found 1 model, 0 tests, 1 snapshot, 0 analyses, 181 macros, 0 operations, 1 seed file, 6 sources, 0 exposures, 0 metrics
   17:03:23
   17:03:24  Concurrency: 1 threads (target='dev')
   17:03:24
   17:03:24  1 of 1 START seed file imdb_dbt.genre_codes..................................... [RUN]
   17:03:24  1 of 1 OK loaded seed file imdb_dbt.genre_codes................................. [INSERT 21 in 0.65s]
   17:03:24
   17:03:24  Finished running 1 seed in 1.62s.
   17:03:24
   17:03:24  Completed successfully
   17:03:24
   17:03:24  Done. PASS=1 WARN=0 ERROR=0 SKIP=0 TOTAL=1
   ```

3. Confirme que se hayan cargado:

   ```sql theme={null}
   SELECT * FROM imdb_dbt.genre_codes LIMIT 10;
   ```

   ```response theme={null}
   +-------+----+
   |genre  |code|
   +-------+----+
   |Drama  |DRA |
   |Romance|ROM |
   |Short  |SHO |
   |Mystery|MYS |
   |Adult  |ADU |
   |Family |FAM |

   |Action |ACT |
   |Sci-Fi |SCI |
   |Horror |HOR |
   |War    |WAR |
   +-------+----+=
   ```

<div id="further-information">
  ## Más información
</div>

Las guías anteriores solo ofrecen una visión general de la funcionalidad de dbt. Se recomienda a los usuarios consultar la excelente [documentación de dbt](https://docs.getdbt.com/docs/introduction).
