← Все курсы · Курс SQL · Все руководства
Как согласовать две таблицы в SQL, если количество строк совпадает
К концу этой страницы вы сможете сравнить две версии одной и той же таблицы и точно сказать, в чем разница: какие строки существуют только с одной стороны, какие строки существуют с обеих сторон, но не совпадают, в каких столбцах находятся разногласия и сколько денег все это занимает. Прошло около двадцати пяти минут, и каждый запрос и результат, представленные ниже, были выполнены.
Вот что нужно сделать сегодня с любыми двумя таблицами, которые должны совпадать. После сравнения количества строк также сравните общее количество. Выберите самый важный числовой столбец и просуммируйте его с обеих сторон. Подсчет строк — это проверка, которая проходит, пока данные неверны, а сумма по тем же строкам — это тридцать секунд дополнительной работы, которая выявляет целый класс ошибок, которые счетчик не видит.
Краткая версия: сопоставьте две таблицы по их ключу с помощью полного внешнего соединения, затем классифицируйте каждый ключ как сопоставленный, измененный, только исходный или только целевой.
Идея состоит в том, чтобы две стопки одинаковой высоты не содержали одинаковых строк, поэтому картина понятна.
- Проверка, которая проходит и ничего не доказывает
- Матч на ключе в обоих направлениях
- Сравните значения в совпадающих строках
- Одно резюме, в котором все сказано
- Столбец за столбцом
- Различия, которые не увидит знак равенства
- Отпечатки пальцев: один номер на стол
- Что делать с тем, что вы найдете
- Полное до и после
- Крайние случаи, которые нарушают примирение
- Почему это работает
- Использование этого в вашем собственном проекте
- Все на одном экране
1. Проверка, которая проходит и ничего не доказывает
Перед пояснением: таблица была скопирована из одной системы в другую. Скажите, что вы бы проверили в первую очередь.
Почти все говорят о количестве строк, и вот оно.
source_rows | target_rows
16 | 16
Шестнадцать против шестнадцати. В большинстве случаев миграции именно на этом проверка прекращается, заявка закрывается, а проблема всплывает через шесть недель в финансовом отчете.
Теперь еще один запрос к тем же двум таблицам.
source_revenue | target_revenue | difference
9890 | 9490 | 400
Четыреста не хватает, у экземпляра с нужным количеством рядов. Строки и значения — это две разные вещи, и счетчик может ответить только на вопрос о строках.
Причина в том, что счет не бесполезен, он просто первый. Подсчет строк выявляет самые большие сбои: ничего не загружено, наполовину загружено, все загружено дважды. Он не может уловить ничего, что оставляет подсчет нетронутым, и оставшаяся часть этой страницы посвящена тому, насколько это важно.
2. Матч по ключу в обоих направлениях.
Для сравнения строк вам нужен ключ: столбец или набор столбцов, который идентифицирует один и тот же реальный объект в обеих таблицах. Тогда полное внешнее соединение по этому ключу, который сохраняет строки, которые не совпадают с любой стороны, и один взгляд на то, где находятся нули.
SELECT COALESCE(s.order_id, t.order_id) AS order_id,
CASE WHEN t.order_id IS NULL THEN 'source only'
WHEN s.order_id IS NULL THEN 'target only' END AS where_it_is
FROM orders s
FULL OUTER JOIN orders_target t USING (order_id)
WHERE s.order_id IS NULL OR t.order_id IS NULL;
1008 | source only
1017 | target only
Приказ 1008 существует в исходнике, а не в копии: он утерян. Заказ 1017 существует в копии, а не в исходнике: он был придуман, что обычно означает, что загрузка выполнялась дважды или тестовая запись ускользнула.
Один проиграл, другой выиграл, так что счет стал равным. Это не совпадение, придуманное для этой страницы; это обычная форма неудачного повтора, и именно поэтому граф согласился.
Здесь всю работу выполняет полное внешнее соединение, и стоит пояснить, почему внутреннее соединение не работает: внутреннее соединение сохраняет только совпадающие строки, а это набор, который не содержит ни одной из ваших проблем. Если объединения еще не выполняются автоматически, руководство по объединениям покрывает, какие строки сохраняет каждый вид.
В некоторых двигателях отсутствует FULL OUTER JOIN. Портативная замена — это два антисоединения, по одному в каждом направлении, в сочетании с UNION ALL, с использованием NOT EXISTS скорее, чем NOT IN по причине в НУЛЕВОЕ руководство.
3. Сравните значения в совпадающих строках.
Строки, присутствующие с обеих сторон, все равно могут не совпадать, поэтому объедините их и сравните каждый столбец.
SELECT s.order_id, s.region, t.region, s.units, t.units
FROM orders s JOIN orders_target t USING (order_id)
WHERE s.region IS DISTINCT FROM t.region
OR s.units IS DISTINCT FROM t.units
OR s.unit_price IS DISTINCT FROM t.unit_price
OR s.product IS DISTINCT FROM t.product
OR s.rep IS DISTINCT FROM t.rep
OR s.order_date IS DISTINCT FROM t.order_date;
1003 | East | east | 3 | 3
1010 | East | East | 5 | 4
Два ряда. Приказ 1003 имеет те же номера и другое написание региона. Приказ 1010 имеет пять единиц в исходнике и четыре в копии, куда и пошли недостающие 400: один стол в 220 стал четырьмя единицами вместо пяти, потеря 220, плюс потерянные 480 в приказе 1008 и изобретенные 300 в приказе 1017. Эту арифметику стоит произнести вслух, потому что способность точно объяснить число 400 — это разница между открытием и беспокойством: 480 снаружи, 300 внутри, 220 выбриты, что в сумме дает 400.
IS DISTINCT FROM это оператор, который следует использовать, а не <>, и разница имеет значение. Равнина <> сравнение с нулевым значением возвращает значение «неизвестно», а не «истина», поэтому значение, которое было заполнено в источнике, и значение, равное нулю в копии, не будет помечено. IS DISTINCT FROM рассматривает нулевой как значение, как и любое другое, и возвращает Верно для этого случая, а это именно то, что нужно для сверки.
4. Одно резюме, которое говорит все
Два запроса выше — это то, на что вы смотрите; это то, что вы сообщаете. Одна строка, четыре числа, и они должны сложиться.
matched | changed | source_only | target_only | total_keys
13 | 2 | 1 | 1 | 17
Тринадцать рядов одинаковы с обеих сторон. Двое существуют на обоих и не согласны. Один только в источнике и один только в цели. Было задействовано семнадцать различных ключей, что больше, чем есть в каждой таблице, поскольку объединение двух таблиц из шестнадцати строк, отличающихся на одну строку в каждую сторону, равно семнадцати.
Проверьте это в обоих направлениях: 13 + 2 + 1 только источник равен 16, количество источников. 13 + 2 + 1 только цель равна 16, количество целей. Если эти два сложения не соответствуют двум подсчетам строк, ключ не уникален, и об этом рассказывается в десятом разделе.
Эта таблица является результатом. «С миграцией все в порядке» — это не утверждение, на основании которого можно действовать; «Тринадцать строк идентичны, две изменены, одна потеряна, одна добавлена, а чистая разница в доходах составляет 400» — это записка о передаче, и для ее выполнения требуется один запрос.
5. Столбец за столбцом
Как только вы узнаете, что две строки не совпадают, следующий полезный вопрос — в каких столбцах, потому что это указывает на причину, а не на симптом.
order_date | rep | region | product | units | unit_price
0 | 0 | 1 | 0 | 1 | 0
Одно разногласие в регионе и одно в подразделениях, и больше нигде. Эта форма сообщает вам то, чего нет в списке плохих строк: это не разрывная загрузка, поскольку разрывная загрузка повреждает столбец во многих строках. Два разных столбца, каждый из которых ошибочен в одной строке, выглядят как два отдельных инцидента, вероятно, ручное редактирование и транскрипция.
Сравните это с формой, которую вы бы увидели, если бы, скажем, каждая дата была сдвинута на день: order_date будет читаться 16, а все остальное 0, и вы будете смотреть на часовые пояса, а не на строки. Тот же запрос, совершенно другой вывод, и их отличает количество столбцов, а не список строк.
Скажите вслух, что бы вы заподозрили, если бы unit_price вернулся как 14 из 16. При загрузке применялось округление или валютная разница, и вам приходилось читать код загрузки, а не данные.
6. Разницы, которую не увидит знак равенства
Перед пояснением: регион по порядку 1003 — «Восток» в одной таблице и «Восток» в другой. Скажите, замечает ли сравнение SQL.
'East' = 'east' false
LOWER('East') = LOWER('east') true
В этом движке сравнение строк чувствительно к регистру, поэтому оно было уловлено. Не каждый механизм сортировки или сортировки столбцов ведет себя таким образом, и при сортировке без учета регистра эта строка будет указана как совпадающая. Какой общий урок: примирение столь же строго, как и его сравнение. Прежде чем доверять чистому результату, проверьте само сравнение на известной разнице.
При наивном сравнении обычно ускользают четыре вида различий. Случай, как указано выше. Завершающий пробел, где «Север» и «Север» отличаются для компьютера, а не для человека. Числовая точность: 10,00 и 10,004 отображаются как 10,00. И даты, несущие в себе время, где один и тот же день в разные часы сравнивается неодинаково.
Стоит обратить внимание и на более тонкую ошибку, потому что именно это происходит, когда люди примиряются, группируясь, а не используя ключ. Сравнение количества регионов в двух таблицах дает следующее:
region | source_rows | target_rows
East | 4 | 3
North | 5 | 5
South | 4 | 4
West | 3 | 3
east | NULL | 1
Появился пятый регион. Общие итоги по-прежнему равны шестнадцати, все отдельные строки присутствуют, и теперь в отчете есть категория, о которой никто никогда не слышал. Это тот же режим отказа, что и для любого почти повторяющегося значения, и процедура очистки для него находится в режиме найти персонажа, которого не видишь.
7. Отпечатки пальцев: по одному номеру на стол.
Соединение двух таблиц построчно — это нормально при шестнадцати строках и дорого при двухстах миллионах, особенно когда они находятся в разных системах и для сравнения одну из них необходимо отправить по сети.
Более дешевый ход — это отпечаток пальца: хэшируйте каждую строку в число и складывайте числа. При сложении не учитывается порядок, поэтому две таблицы, содержащие одинаковые строки в разном порядке, дают одинаковую сумму.
SELECT SUM(HASH(order_id, order_date, rep, region, product, units, unit_price)) FROM orders;
source 128147106948374048609
target 136663960016580492445
Разные, поэтому таблицы различаются, и для каждой стороны потребовался один проход, при этом ничего не переносилось, кроме одного числа. Это ничего не говорит вам о том, где именно ведется торговля.
Вы получаете информацию о том, где именно, снимая отпечатки пальцев с групп, а не со всей таблицы. Здесь по регионам:
region | same
East | false
North | true
South | true
West | false
east | false
Север и Юг доказуемо идентичны и не нуждаются в дополнительной проверке. Все по-другому на Востоке, Западе и в призрачном регионе. В большой таблице вы берете отпечатки пальцев по месяцам или по диапазону ключей, находите несколько сегментов, которые отличаются, и только затем выполняете сравнение на уровне строк. В этом вся стратегия: дешево сравнить, сузить, а потом присмотреться.
Одно предостережение. Хэш-функции различаются в зависимости от движка, поэтому отпечаток пальца, вычисленный в Постгрес, не будет совпадать с отпечатком пальца, вычисленным в SQL Server, а для сравнения между системами требуется хэш, который могут вычислить оба, обычно MD5 объединенной, последовательно отформатированной строки. Большая часть этой работы — добиться идентичного форматирования с обеих сторон.
8. Что делать с найденным
Примирение, которое заканчивается списком разногласий, — это полдела. У каждой категории свой следующий ход.
| Нахождение | Обычно означает | Следующий ход |
|---|---|---|
| Только источник | Строки удалены фильтром, неудачным пакетом или ошибкой преобразования типа. | Найдите закономерность: все ли это одна дата, один регион, один продукт? |
| Только таргетинг | Нагрузка, запускавшаяся дважды, или тестовые данные | Проверьте, являются ли они дубликатами настоящих ключей или совершенно новыми. |
| Изменено, один столбец, много строк | Преобразование, применяемое при загрузке | Прочтите логику загрузки. Это вопрос кода, а не вопроса данных. |
| Изменено, много столбцов, мало строк | Ручное редактирование после загрузки | Узнайте, кому разрешено редактировать цель напрямую |
И записывайте то, что вы нашли, даже если оно небольшое, потому что для человека, получающего систему, важно число, а не количество строк. «Четыреста из 9890, что составляет 4,0%, в две строки, оба объяснены» — это предложение, которое кто-то может подписать. Пробный прогон миграции существует для того, чтобы выработать именно это предложение, прежде чем кто-либо станет зависеть от новой системы, и руководство по пробному запуску вот где это вписывается в более широкий процесс.
Полное до и после
Те же две таблицы, тот же вопрос: сработала ли копия?
До
SELECT COUNT(*) FROM orders; -- 16
SELECT COUNT(*) FROM orders_target; -- 16
-- "counts match, we are good"
Прошел, закрылся и ошибся. Четыреста из 9890 отсутствуют, один заказ исчез, один заказ, которого никогда не было, теперь в системе, в одном заказе неверное количество и создан пятый регион.
после
-- 1. counts and a total, together
16 rows / 9,890 against 16 rows / 9,490 difference 400
-- 2. keys on one side only
1008 source only, 1017 target only
-- 3. matched rows that disagree
1003 region East vs east, 1010 units 5 vs 4
-- 4. the summary, which must add back to both row counts
matched 13, changed 2, source only 1, target only 1, keys 17
13 + 2 + 1 = 16 on each side
-- 5. which columns
region 1, units 1, everything else 0
-- 6. the arithmetic of the difference
-480 lost, +300 invented, -220 shaved = -400
Утверждение, и именно поэтому второй запрос не является необязательным: подсчет строк 16 против 16 был верным, обнадеживающим и совершенно слепым к потерянной строке, изобретенной строке, измененному количеству и изобретенной области.
Крайние случаи, которые нарушают примирение
Шесть, которые стоит знать.
Ключ не уникален. Если у какой-либо стороны есть ключ дважды, соединение умножается, и каждый последующий подсчет будет неправильным. Сначала проверьте обе стороны: COUNT(*) против COUNT(DISTINCT key), на каждом столе, прежде чем присоединиться к чему-либо.
Ключ не тот ключ. Перенумерация систем. Если цель назначила свои собственные идентификаторы, вы сопоставляете естественный ключ, а если такового нет, вы перешли в Разрешение объекта.
Законные различия. Цель может намеренно исключать отмененные записи или хранить другой формат даты. Прежде чем проводить сравнение, согласуйте ожидаемые различия в письменном виде, иначе вы сообщите о них как об ошибках.
Сравнивая в разные моменты. Живой источник изменяется во время его проверки, поэтому разница в нескольких последних строках может быть скорее временем, чем ошибкой. Сначала сделайте снимок обеих сторон, вот что временная таблица это для.
Плавающая точка. Две системы могут вычислить одинаковую сумму и не согласиться в пятнадцатом десятичном знаке. Прежде чем сравнивать деньги, округлите их до точности, которая важна для бизнеса.
Нулевые значения по сравнению с неправильным оператором. <> молча пропускает изменение значения на нулевое. Использовать IS DISTINCT FROM, а если в вашем движке его нет, COALESCE обе стороны к контрольному значению, которое не может встречаться в данных.
Почему это работает
Причина, по которой количество строк кажется достаточным, а не является таковым, заключается в том, что оно измеряет одно измерение качества, а их несколько. Структуры оценки качества данных последовательно разбивают их на отдельные измерения, полноту, точность, последовательность и т. д. именно потому, что они не работают независимо друг от друга, а показатель одного ничего не говорит о других (Внутренний, Каппьелло, Франкаланчи & Маурино, 2009, Обзоры вычислительной техники ACM, 41(3), 1–52). Подсчет строк — это проверка полноты, причем слабая: она обнаруживает недостающие строки только тогда, когда на их место ничего не поступает. Каждая из четырех проверок на этой странице охватывает разные аспекты, поэтому они обнаруживают четыре разные проблемы и почему выполнение одной из них не составляет большей части работы.
Стратегия отпечатков пальцев, описанная в седьмом разделе, — это старая идея, имеющая название. Если вы хешируете небольшие фрагменты данных, а затем объединяете эти хэши, вы можете сравнить две большие коллекции, почти ничего не меняя, а затем сузить область до отличающейся части, перейдя к любому объединенному хешу, который не совпадает; это иерархическое сравнение является основой конструкции Меркла и именно поэтому каждая крупномасштабная система синхронизации использует ту или иную его версию (Меркл, 1988, Достижения в криптологии — КРИПТО '87, 369–378). Отпечатки всей таблицы, затем по регионам, а затем по строкам — это идея, реализуемая вручную с помощью трех запросов.
Одно примечание к вопросам, заданным на этой странице, перед каждым ответом. Объяснение шага самому себе до того, как объяснение будет показано, улучшит то, что вы из него сохраните, поэтому стоит угадать количество строк, прежде чем читать общее количество (Чи, Басок, Льюис, Рейманн & Глейзер, 1989, Когнитивная наука, 13(2), 145–182).
Использование этого в вашем собственном проекте
Полное сравнение огромной таблицы на уровне строк является дорогостоящим и обычно ненужным. Вместо этого сделайте это по порядку.
- Убедитесь, что ключ уникален с обеих сторон прежде всего. От этого зависит каждое число после этого.
- Сравните количество и сумму вместе. Никогда не считайте в одиночку.
- Отпечаток пальца от естественного ведра, месяц, регион или диапазон ключей и найдите сегменты, которые отличаются.
- Полное внешнее соединение только внутри этих сегментови классифицируйте каждый ключ по четырем категориям.
- Подсчитайте разногласия в каждом столбце, потому что форма называет причину.
- Напишите резюме в виде предложения, в котором есть деньги.и перечислите ожидаемые различия отдельно от неожиданных.
Если у вас под рукой есть бумага, один дополнительный эскиз стоит пяти минут. Нарисуйте два прямоугольника и между ними напишите четыре категории: совпадающие, измененные, только левые, только правые. Затем напишите рядом с каждым, какое число вы ожидаете получить, если бы копия была идеальной. Три из них равны нулю, и если вы укажете это явно, прежде чем что-либо запускать, это не позволит объяснить ненулевое значение.
Все на одном экране
Это лист поиска. Закройте правую колонку, проработайте левую и произнесите каждый ответ вслух, прежде чем проверять его.
| вещь | Что он делает |
|---|---|
| Количество строк | Ничего не ловит загружено, загружено наполовину, загружено вдвое. Ничего больше. |
| Посчитайте плюс итог | Ловит весь класс, который граф не может. Тридцать секунд. |
| Первая проверка всего | Ключевые уникальности с обеих сторон: COUNT(*) против COUNT(DISTINCT key). |
| Нахождение односторонних строк | FULL OUTER JOIN, затем посмотрите, где находятся нули. |
| Нет полного внешнего соединения | два NOT EXISTS анти-объединения с UNION ALL. |
| Сравнение значений | IS DISTINCT FROM, никогда <>. |
| Почему | <> относительно нуля неизвестно, поэтому строка не помечается. |
| Четыре категории | Сопоставлено, изменено, только источник, только цель. |
| Примирение | Совпадение + изменение + одностороннее должно равняться количеству каждой строки. |
| Количество по столбцу | Один столбец, много строк — это ошибка загрузки. Многие столбцы и несколько строк — это редактирование вручную. |
| Регистр и пробелы | Может быть перехвачен или не пойман, в зависимости от сопоставления. Проверьте это. |
| Групповое сравнение | Создает категорию, когда значение различается в зависимости от регистра. Итоги по-прежнему совпадают. |
| Отпечаток пальца | SUM(HASH(cols)). Заказ независимый, один номер, без детализации. |
| Локализация | Отпечаток пальца для каждого сегмента, затем сравнивайте строки только по сегментам, которые отличаются. |
| По системам | Хэш-функции различаются. Используйте MD5 строки идентичного формата. |
| Результат | Предложение с деньгами и ожидаемыми различиями, перечисленными отдельно. |
Еще одна мысль, и мне бы искренне хотелось услышать ответы других людей. Тот, который я помню, был миграцией, подписанной при совпадении количества строк, где при повторном запуске был удален один пакет и добавлен другой, и оба оказались почти одинакового размера; Разница обнаружилась четыре месяца спустя, когда поставщик гонялся за счетом, которого больше не существовало ни в одной из систем. Что скрывает от вас количество совпадающих строк?
Ссылки
- Батини К., Каппиелло К., Франкаланчи К. и Маурино А. (2009). Методологии оценки и улучшения качества данных. Обзоры вычислительной техники ACM, 41(3), 1–52.
- Меркл, RC (1988). Цифровая подпись, основанная на обычной функции шифрования. Достижения в криптологии — КРИПТО '87, 369–378.
- Чи, М.Т.Х., Бассок, М., Льюис, М.В., Рейманн, П. и Глейзер, Р. (1989). Самообъяснения: как учащиеся изучают и используют примеры при обучении решению проблем. Когнитивная наука, 13(2), 145–182.
SQL для аналитиков это 458 страниц, полное внешнее соединение, сравнение значений и отпечаток пальца, который определяет, где две таблицы расходятся.
SQL для аналитиков, 19 $ →тот Курс SQL дает вам повторы: копии, поврежденные способами, которые вам нужно найти, полные внешние соединения для классификации и отпечатки пальцев, которые нужно сузить, а также флэш-карты, на которых оператор сравнения улавливает, какой тип разницы. Теперь вы владеете методом; комплект - это практика. Ничего устанавливать, учетная запись не требуется.
Откройте набор SQL →