SQL Наружное соединение на 3 таблицы в SQL - PullRequest
0 голосов
/ 04 мая 2020

Я не могу обернуться вокруг этого.

Данные представляют собой 3 таблицы (прогноз A / B / C) с 3 соответствующими столбцами - метка времени, значение, страна. Некоторые строки могут отсутствовать в любой из этих таблиц. Я хочу сделать полное внешнее объединение для всех трех таблиц на основе столбца Timestamp. Данные выглядят следующим образом:

+-----------------------------+-------+---------+
| Timestamp                   | Value | Country |
+-----------------------------+-------+---------+
| 2020-05-05 21:00:00.0000000 | 19,02 | FR      |
+-----------------------------+-------+---------+
| 2020-05-05 20:00:00.0000000 | 17,85 | FR      |
+-----------------------------+-------+---------+
| 2020-05-05 19:00:00.0000000 | 17,71 | FR      |
+-----------------------------+-------+---------+
            ... omitted for brevity ...
+-----------------------------+-------+---------+
| 2020-05-05 01:00:00.0000000 | 3,33  | FR      |
+-----------------------------+-------+---------+
| 2020-05-05 00:00:00.0000000 | 5,89  | FR      |
+-----------------------------+-------+---------+
| 2020-05-04 23:00:00.0000000 | 7,62  | FR      |
+-----------------------------+-------+---------+
| 2020-05-04 22:00:00.0000000 | 11,79 | FR      |
+-----------------------------+-------+---------+

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

+-----------------------------+--------+-------+-------+
| Timestamp                   | ValueA | ValueB| ValueC|
+-----------------------------+--------+-------+-------+
| 2020-05-05 21:00:00.0000000 | -28,78 | 38,89 | 80    |
+-----------------------------+--------+-------+-------+
| 2020-05-05 20:00:00.0000000 | 23,78  | 19,02 | 120   |
+-----------------------------+--------+-------+-------+
| 2020-05-05 19:00:00.0000000 | -13,78 | 32,36 | 71    |
+-----------------------------+--------+-------+-------+
                    ... omited for brevity ...
+-----------------------------+--------+-------+-------+
| 2020-05-04 18:00:00.0000000 | 18,65  | 31,24 | 51    |
+-----------------------------+--------+-------+-------+
| 2020-05-04 17:00:00.0000000 | 11,51  | null  | 14     |
+-----------------------------+--------+-------+-------+
| 2020-05-04 16:00:00.0000000 | 1,51   | 34,07  | 23    |
+-----------------------------+--------+-------+-------+
| 2020-05-04 15:00:00.0000000 | 21,51  | null  | null  |
+-----------------------------+--------+-------+-------+

Вот что я попробовал:

Решение A:

SELECT  A.Timestamp,
        A.Value, 
        B.Value,
        C.Value
FROM(
    (SELECT Timestamp, Value, Country FROM [dbo].PrognosisA) A
    FULL OUTER JOIN 
    (SELECT Timestamp, Value, Country FROM [dbo].PrognosisB) B on A.Timestamp = B.Timestamp
    FULL OUTER JOIN 
    (SELECT Timestamp, Value, Country FROM [dbo].PrognosisC) C on ISNULL(A.Timestamp, B.Timestamp) = C.Timestamp
)
WHERE  A.Country = 'FR' AND B.Country = 'FR' AND C.Country = 'FR'
ORDER BY A.Timestamp DESC

Однако, Это решение возвращает дублированные метки времени.

Решение B:

select allTimestamp.Timestamp, [dbo].PrognosisB.Value, [dbo].PrognosisA.Value, [dbo].PrognosisC.Value
from (select Timestamp, Country from [dbo].PrognosisB union 
      select Timestamp, Country from [dbo].PrognosisA union
      select Timestamp, Country from [dbo].PrognosisC
     ) allTimestamp left outer join 
     [dbo].PrognosisB on allTimestamp.Timestamp = [dbo].PrognosisB.Timestamp left outer join
     [dbo].PrognosisA on allTimestamp.Timestamp = [dbo].PrognosisA.Timestamp left outer join
     [dbo].PrognosisC on allTimestamp.Timestamp = [dbo].PrognosisC.Timestamp
WHERE allTimestamp.Country = 'FR'
ORDER BY allTimestamp.Timestamp DESC;

Это решение возвращает еще больше дубликатов.

Так куда я иду не так?

Ответы [ 3 ]

2 голосов
/ 04 мая 2020

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

Я настроил данные тестирования следующим образом, используя INT вместо даты и времени для ясности:

/*

DROP TABLE PrognosisA
DROP TABLE PrognosisB
DROP TABLE PrognosisC

*/

CREATE TABLE PrognosisA
 (
   Timestamp  int            not null  primary key
  ,Value      decimal(10,2)  not null
  ,Country    char(2)        not null
 )

CREATE TABLE PrognosisB
 (
   Timestamp  int            not null  primary key
  ,Value      decimal(10,2)  not null
  ,Country    char(2)        not null
 )

CREATE TABLE PrognosisC
 (
   Timestamp  int            not null  primary key
  ,Value      decimal(10,2)  not null
  ,Country    char(2)        not null
 )

Затем я настроил данные, которые охватывают все возможные комбинации «ID в одном, нескольких или все таблицы »:

INSERT PrognosisA (Timestamp, Value, Country) values
  (1, 1.11, 'FR')
 ,(2, 2.21, 'FR')
 ,(3, 3.31, 'FR')
 ,(5, 5.51, 'FR')

INSERT PrognosisB (Timestamp, Value, Country) values
  (1, 1.12, 'FR')
 ,(2, 2.22, 'FR')
 ,(4, 4.42, 'FR')
 ,(6, 6.62, 'FR')

INSERT PrognosisC (Timestamp, Value, Country) values
  (1, 1.13, 'FR')
 ,(3, 3.33, 'FR')
 ,(4, 4.43, 'FR')
 ,(7, 7.73, 'FR')

После нескольких тупиков (полные внешние объединения всегда являются проблемой), я получил следующее:

SELECT
   coalesce(pa.Timestamp, pb.Timestamp, pc.Timestamp)   Timestamp
  ,pa.Timestamp
  ,pb.Timestamp
  ,pc.Timestamp
  ,pa.Value
  ,pb.Value
  ,pc.Value
 from PrognosisA  pa
  full outer join PrognosisB  pb
   on pb.Timestamp = pa.Timestamp
  full outer join PrognosisC  pc
   on pc.Timestamp = isnull(pa.Timestamp, pb.TimeStamp)
 where coalesce(pa.Country, pb.Country, pc.Country) = 'FR'
 order by 1  --  Only for testing!

Это возвращает все данные со всеми возможными перестановками присутствующих / не присутствующих и без повторяющихся строк.

Две рекомендации:

  • Просмотрите ваши данные Timstamp - и их типы данных - очень тщательно. Даты, особенно когда они равны go с точностью до Nths секунды, могут быть очень суетливыми для работы и не будут моим первым выбором для столбца соединения. Возможно, точность варьируется между таблицами и что-то усекается?

  • Данные, как описано, могут быть намного лучше нормализованы. Сделайте это одной таблицей, добавьте столбец для «WhichPrognosis» (A, B, C, что угодно) и go оттуда. Нужно вернуть один столбец «значение» для прогноза? Либо используйте сводную инструкцию (неудобно), либо позвольте вызывающему приложению разобраться с форматированием данных.

0 голосов
/ 04 мая 2020

Я бы предложил перенести сравнение стран в предложение объединения следующим образом:

не проверено

SELECT  isnull(A.Timestamp, isnull(B.Timestamp, C.Timestamp)) Timestamp
   ,A.Value ValueA, B.Value ValueB, C.Value ValueC
FROM ( -- subquery to get only records where Country = 'FR'
   SELECT *
   FROM [dbo].PrognosisA
   WHERE Country = 'FR'  -- limit to records where Country = 'FR'
) A
FULL OUTER JOIN [dbo].PrognosisB) B
   on A.Timestamp = B.Timestamp
   and B.Country = 'FR' -- limit to records where Country = 'FR'
FULL OUTER JOIN [dbo].PrognosisC C
   on ISNULL(A.Timestamp, B.Timestamp) = C.Timestamp
   and C.Country = 'FR' -- limit to records where Country = 'FR'
ORDER BY isnull(A.Timestamp, isnull(B.Timestamp, C.Timestamp)) DESC

Если в любой из 3 таблиц есть дубликаты отметки времени (где country = 'fr'), тогда, конечно, в вашей объединенной таблице будут дубликаты.

Если вы хотите удалить дубликаты, вы можете использовать DISTINCT или GROUP BY различными способами.

0 голосов
/ 04 мая 2020

Пока у вас есть только одна строка на TimeStamp на страну, вы можете попробовать что-то вроде этого.

;WITH TimeStamps AS (
           SELECT Timestamp, County FROM [dbo].PrognosisA  
     union SELECT Timestamp, County FROM [dbo].PrognosisB
     union SELECT Timestamp, County FROM [dbo].PrognosisC
 )

SELECT TimeStamps.Timestamp,
       A.Value,
       B.Value,
       C.Value
FROM   TimeStamps
       LEFT JOIN [dbo].PrognosisA A
            ON  TimeStamps.TimeStamp = A.Timestamp AND TimeStamps.Country = A.Country
       LEFT JOIN [dbo].PrognosisB B
            ON  TimeStamps.TimeStamp = B.Timestamp AND TimeStamps.Country = B.Country
       LEFT JOIN [dbo].PrognosisC C
            ON  TimeStamps.TimeStamp = C.Timestamp AND TimeStamps.Country = C.Country
WHERE  TimeStamps.Country = 'FR'
ORDER BY
       TimeStamps.Timestamp DESC

Если у вас есть 2 или более [TimeStamp, Country], вы будете получать все больше и больше дубликаты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...