
# Combinación a nivel de columna

La combinación a nivel de columna es la principal ventaja de merql frente a la comparación ingenua a nivel de fila. Cuando ambas partes modifican la misma fila, merql compara cada columna por separado en vez de marcar toda la fila como conflicto.

## El problema de combinar a nivel de fila

Considere una tabla `posts` en la que dos personas editan la misma fila:

```
Base:    post #42  { title: "Hello",     content: "Body",    status: "draft"   }
Ours:    post #42  { title: "Hello",     content: "Body v2", status: "draft"   }
Theirs:  post #42  { title: "New Title", content: "Body",    status: "publish" }
```

Una combinación a nivel de fila detecta que ambas partes modificaron la fila y declara un conflicto. Pero los cambios no se solapan. Nuestro lado editó `content`; el suyo, `title` y `status`.

## Cómo lo resuelve la combinación a nivel de columna

merql aplica las mismas reglas de combinación de tres vías a cada columna por separado:

```
title:   base="Hello", ours="Hello",  theirs="New Title"  -> accept theirs
content: base="Body",  ours="Body v2", theirs="Body"      -> accept ours
status:  base="draft", ours="draft",  theirs="publish"    -> accept theirs
```

Resultado: `{ title: "New Title", content: "Body v2", status: "publish" }`. Combinación limpia, sin conflictos.

## Cómo funciona

Cuando `ThreeWayMerge` detecta que ambas partes actualizaron la misma fila, delega en `ColumnMerge::merge()`:

```php
use Merql\Merge\ColumnMerge;

$result = ColumnMerge::merge(
    table: 'posts',
    rowKey: '42',
    base: ['title' => 'Hello', 'content' => 'Body', 'status' => 'draft'],
    ours: ['title' => 'Hello', 'content' => 'Body v2', 'status' => 'draft'],
    theirs: ['title' => 'New Title', 'content' => 'Body', 'status' => 'publish'],
);

// $result['values']    -> merged column values
// $result['conflicts'] -> list of Conflict objects (empty if clean)
```

Se aplican las mismas reglas a cada columna:

| Base | Nuestro lado | Su lado | Resultado |
|---|---|---|---|
| A | A | A | Conservar la base (sin cambios) |
| A | A | B | Aceptar el valor de su lado |
| A | B | A | Aceptar el valor de nuestro lado |
| A | B | B | Aceptar (ambos coinciden) |
| A | B | C | **Conflicto** (valores distintos) |

## Cuándo siguen produciéndose conflictos

Se produce un conflicto a nivel de columna cuando ambas partes modifican la misma columna con valores distintos:

```
Base:    post #42  { title: "Hello" }
Ours:    post #42  { title: "Welcome" }
Theirs:  post #42  { title: "Greetings" }
```

Ambas partes cambiaron `title` por valores distintos. Es un conflicto real que requiere resolución.

```php
$result = ColumnMerge::merge(
    'posts', '42',
    ['title' => 'Hello'],
    ['title' => 'Welcome'],
    ['title' => 'Greetings'],
);

// $result['conflicts'] contains one Conflict:
//   table: "posts", column: "title"
//   oursValue: "Welcome", theirsValue: "Greetings", baseValue: "Hello"
```

## Conflictos parciales

Una misma fila puede contener combinaciones sin conflictos y conflictos. Si una columna se combina sin conflictos y otra entra en conflicto, el `MergeOperation` de la fila contiene los valores combinados de las columnas sin conflictos y, de forma predeterminada, el valor de nuestro lado en las columnas en conflicto. El conflicto se informa por separado.

```
Base:    { title: "Hello", content: "Body",    status: "draft" }
Ours:    { title: "Welcome", content: "Body v2", status: "draft" }
Theirs:  { title: "Greetings", content: "Body",    status: "publish" }
```

Aquí `title` entra en conflicto (ambas partes la cambiaron de forma distinta), `content` se combina sin conflictos (solo cambió nuestro lado) y `status` se combina sin conflictos (solo cambió su lado). La operación contiene:

```php
// values: { title: "Welcome", content: "Body v2", status: "publish" }
// One conflict on the "title" column.
```

## Comparación de valores

Los valores de las columnas se comparan como cadenas después de convertirlos. `NULL` es un valor distinto:

- `NULL` a `NULL` = sin cambios.
- `NULL` a `"hello"` = actualización.
- `"hello"` a `NULL` = actualización.
- `"1"` a `1` = sin cambios (ambos se convierten en la cadena `"1"`).

Esto coincide con la semántica de recuperación del adaptador de conexión MerQL: todos los valores de la base de datos se devuelven como cadenas (o NULL).

## Combinación a nivel de celda

Cuando la combinación a nivel de columna sigue produciendo un conflicto (ambas partes cambiaron la misma columna con valores distintos), merql puede profundizar un nivel más con la [combinación a nivel de celda](https://merql.inline0.dev/es/docs/merge/cell-level). Esto se aplica a columnas TEXT (diferencias línea por línea) y columnas JSON (diferencias clave por clave), y resuelve los casos en los que ambas partes editaron distintas partes del valor de una misma columna.
