Я хотел бы проверить в нескольких таблицах, что одинаковые ключи / одинаковое количество ключей присутствуют в каждой из таблиц.

В настоящее время я создал решение, которое проверяет количество ключей для каждой отдельной таблицы, проверяет количество ключей, когда все таблицы объединяются вместе, а затем сравнивает.

Это решение работает, но мне интересно, есть ли более оптимальное решение ...

Пример решения в его нынешнем виде:

SELECT COUNT(DISTINCT variable) AS num_ids FROM table_a;

SELECT COUNT(DISTINCT variable) AS num_ids FROM table_b;

SELECT COUNT(DISTINCT variable) AS num_ids FROM table_c;

SELECT COUNT(DISTINCT a.variable) AS num_ids
FROM (SELECT DISTINCT VARIABLE FROM table_a) a
  INNER JOIN (SELECT DISTINCT VARIABLE FROM table_b) b ON a.variable = b.variable
  INNER JOIN (SELECT DISTINCT VARIABLE FROM table_c) c ON a.variable = c.variable;

ОБНОВИТЬ:

Сложность, с которой я столкнулся, объединяя это в один запрос, заключается в том, что любая из таблиц может не быть уникальной в ПЕРЕМЕННОЙ, которую я хочу проверить, поэтому мне пришлось использовать отдельный перед слиянием, чтобы избежать расширения объединения.

2
Sam Gilbert 24 Дек 2015 в 14:42

2 ответа

Лучший ответ

Поскольку мы только подсчитываем, думаю, нет необходимости объединять таблицы в столбце variable. UNION должно быть достаточно. Нам по-прежнему приходится использовать DISTINCT, чтобы игнорировать / подавлять дубликаты, что часто означает дополнительную сортировку. Индекс на variable должен помочь получить счетчики для отдельных таблиц, но не поможет получить счетчик объединенной таблицы.

Вот пример сравнения двух таблиц:

WITH
CTE_A
AS
(
    SELECT COUNT(DISTINCT variable) AS CountA
    FROM TableA
)
,CTE_B
AS
(
    SELECT COUNT(DISTINCT variable) AS CountB
    FROM TableB
)
,CTE_AB
AS
(
    SELECT COUNT(DISTINCT variable) AS CountAB
    FROM
    (
        SELECT variable
        FROM TableA

        UNION ALL 
        -- sic! use ALL here to avoid sort when merging two tables
        -- there should be only one distinct sort for the outer `COUNT`

        SELECT variable
        FROM TableB
    ) AS AB
)
SELECT
    CASE WHEN CountA = CountAB AND CountB = CountAB 
    THEN 'same' ELSE 'different' END AS ResultAB
FROM
    CTE_A
    CROSS JOIN CTE_B
    CROSS JOIN CTE_AB
;

Три стола:

WITH
CTE_A
AS
(
    SELECT COUNT(DISTINCT variable) AS CountA
    FROM TableA
)
,CTE_B
AS
(
    SELECT COUNT(DISTINCT variable) AS CountB
    FROM TableB
)
,CTE_C
AS
(
    SELECT COUNT(DISTINCT variable) AS CountC
    FROM TableC
)
,CTE_ABC
AS
(
    SELECT COUNT(DISTINCT variable) AS CountABC
    FROM
    (
        SELECT variable
        FROM TableA

        UNION ALL 
        -- sic! use ALL here to avoid sort when merging two tables
        -- there should be only one distinct sort for the outer `COUNT`

        SELECT variable
        FROM TableB

        UNION ALL 
        -- sic! use ALL here to avoid sort when merging two tables
        -- there should be only one distinct sort for the outer `COUNT`

        SELECT variable
        FROM TableC
    ) AS AB
)
SELECT
    CASE WHEN CountA = CountABC AND CountB = CountABC AND CountC = CountABC 
    THEN 'same' ELSE 'different' END AS ResultABC
FROM
    CTE_A
    CROSS JOIN CTE_B
    CROSS JOIN CTE_C
    CROSS JOIN CTE_ABC
;

Я сознательно выбрал CTE, потому что, насколько мне известно, Postgres материализует CTE, а в нашем случае каждый CTE будет иметь только одну строку.


Использование array_agg с заказом по - еще лучший вариант, если есть на красное смещение. Вам все равно нужно будет использовать DISTINCT, но вам не нужно объединять все таблицы вместе.

WITH
CTE_A
AS
(
    SELECT array_agg(DISTINCT variable ORDER BY variable) AS A
    FROM TableA
)
,CTE_B
AS
(
    SELECT array_agg(DISTINCT variable ORDER BY variable) AS B
    FROM TableB
)
,CTE_C
AS
(
    SELECT array_agg(DISTINCT variable ORDER BY variable) AS C
    FROM TableC
)
SELECT
    CASE WHEN A = B AND B = C
    THEN 'same' ELSE 'different' END AS ResultABC
FROM
    CTE_A
    CROSS JOIN CTE_B
    CROSS JOIN CTE_C
;
2
Vladimir Baranov 9 Мар 2016 в 11:54

Что ж, это, вероятно, самая неприятная часть SQL, которую я мог построить для вас :) Я всегда буду отрицать, что написал это и что моя учетная запись stackoverflow была взломана;)

SELECT
  'All OK'
WHERE
  ( SELECT COUNT(DISTINCT id) FROM table_a ) = ( SELECT COUNT(DISTINCT id) FROM table_b )
  AND ( SELECT COUNT(DISTINCT id) FROM table_b ) = ( SELECT COUNT(DISTINCT id) FROM table_c )

Кстати, это не оптимизирует запрос - он по-прежнему выполняет три запроса (но я думаю, что лучше, чем четыре?).

Поскольку мы только считаем, думаю, нет необходимости объединять таблицы в столбце {{X0}}. {{X1}} должно быть достаточно. Нам все еще нужно использовать {{X2}} для игнорирования / подавления дубликатов, что часто означает дополнительную сортировку. Индекс на {{X3}} должен помочь получить счетчики для отдельных таблиц, но не поможет получить счет в объединенной таблице.

SELECT DISTINCT
  tbl_a.a_count,
  tbl_b.b_count,
  tbl_c.c_count
FROM
  ( SELECT COUNT(id) a_count, array_agg(id order by id) ids FROM table_a) tbl_a,
  ( SELECT COUNT(id) b_count, array_agg(id order by id) ids FROM table_b) tbl_b,
  ( SELECT COUNT(id) c_count, array_agg(id order by id) ids FROM table_c) tbl_c
WHERE
  tbl_a.ids = tbl_b.ids
  AND tbl_b.ids = tbl_c.ids

Вышеупомянутый запрос вернется только в том случае, если все таблицы имеют одинаковое количество строк, гарантируя, что IDS также совпадают.

0
Trent 8 Мар 2016 в 21:06