2016-09-21 3 views
0
A27,346467413,68723601 
A28,346467414,68723601 
A28,346467416,68723601 
A27,349454768,8908697809 
A28,349454769,8908697809 
A28,349454771,8908697809 

У меня есть приведенный выше пример из таблицы. Мне нужно сделать какой-то ошеломляющий sql. В основном, меня спрашивали, что для каждого A27 два прохода A28 принадлежат этому A27, и информация необходимо связать вместе. Тем не менее, меня попросили создать таблицу для каждого A_Number, так что A27 будет в своей таблице, а A28 будет в своей собственной таблице. Мне нужно сделать это, чтобы присоединиться к ним, чтобы каждый A27 мог ссылаться на соответствующий A28, который следует за ним. Просто интересно, как бы я это сделал, если бы это было в отдельных таблицах и как выглядели бы Join и SQL? Имейте в виду, что данные не имеют никакого смысла в качестве средств сверки счетов.ссылочная целостность и сложные соединения

enter image description here

Я загрузил файл образец прилагается

enter image description here

+0

Бит путается, пожалуйста, сообщите об ожидаемом выходе и укажите названия столбцов. – Susang

+0

Из вашего описания в таблице нет ничего, что связывает родителя (A27) с дочерним элементом (A28), отличным от близости. В Oracle поиск данных не является детерминированным - порядок, в который будут извлекаться данные, изменится, если вы не укажете порядок. У вас нет ключа, который вы можете использовать для заказа, так что это будет проблемой. Возможно, SQL Server отличается. –

+0

Предполагая, что вы * можете * надежно связать родителя с дочерним, тогда вам нужно назначить идентификатор для каждого родителя и указать его на дочернем. Вставьте данные в свои две таблицы, и объединение тривиально. –

ответ

1
;WITH cte AS (
    SELECT 
     * 
     ,ROW_NUMBER() OVER (ORDER BY Column2) AS RowNumber 
    FROM 
     @Table 
) 

SELECT 
    * 
    ,DENSE_RANK() OVER (ORDER BY A27.Column2) as GroupingId 
FROM 
    cte A27 
    LEFT JOIN cte A28 
    ON A28.RowNumber IN (a27.RowNumber + 1, a27.RowNumber + 2) 
    AND A28.Column1 = 'A28' 
WHERE 
    A27.Column1 = 'A27' 

Поскольку основанный на изображении Column3 не является уникальным только номера строк, которые вы хотите, я не пошел по пути его использования. Вместо этого я использовал ROW_NUMBER на основе Column2, который выглядит как первичный ключ приращения, а затем просто LEFT SELF JOIN. Это для 2 строк A28 AFTER каждой строки A27. если вы хотите «preceding», как ваш OP говорит, но не предполагает, то просто повернуть его вспять путем изменений JOIN условия:

ON A28.RowNumber IN (a27.RowNumber - 1, a27.RowNumber - 2) 

Примечание Я добавил GroupingID дать вам способ идентификации, который A27 и A28 являются вместе он может использоваться как новый первичный ключ в вашей таблице A27 и внешний ключ в вашей таблице A28 для сохранения отношений.

Вот данные испытаний, которые я использовал.

DECLARE @Table AS TABLE (Column1 CHAR(3), Column2 BIGINT, Column3 BIGINT) 
INSERT INTO @Table VALUES 
('A27',346467408,68723601) 
,('A28',346467409,68723601) 
,('A28',346467411,68723601) 
,('A27',346467413,68723601) 
,('A28',346467414,68723601) 
,('A28',346467416,68723601) 
,('A27',349454768,8908697809) 
,('A28',349454769,8908697809) 
,('A28',349454771,8908697809) 

Edit: В соответствии с Вашим комментарием о том, как это сделать, когда в отдельных таблицах. Вы можете использовать объединение всех объединить таблицы:

DECLARE @A27 AS TABLE (Column1 CHAR(3), Column2 BIGINT, Column3 BIGINT) 
DECLARE @A28 AS TABLE (Column1 CHAR(3), Column2 BIGINT, Column3 BIGINT) 
INSERT INTO @A27 VALUES 
('A27',346467408,68723601) 
,('A27',346467413,68723601) 
,('A27',349454768,8908697809) 
INSERT INTO @A28 VALUES 
('A28',346467409,68723601) 
,('A28',346467411,68723601) 
,('A28',346467414,68723601) 
,('A28',346467416,68723601) 
,('A28',349454769,8908697809) 
,('A28',349454771,8908697809) 

;WITH cteUnion AS (
    SELECT 
     Column1, Column2, Column3 
    FROM 
     @A27 

    UNION ALL 

    SELECT 
     Column1, Column2, Column3 
    FROM 
     @A28 
) 

, cteRowNum AS (
    SELECT * ,ROW_NUMBER() OVER (ORDER BY Column2) AS RowNumber 
    FROM 
     cteUnion 
) 

SELECT *, DENSE_RANK() OVER (ORDER BY a27.Column2) as GroupingId 
FROM 
    cteRowNum a27 
    LEFT JOIN cteRowNum a28 
    ON a28.RowNumber IN (a27.RowNumber + 1, a27.RowNumber + 2) 
    AND a28.Column1 = 'A28' 
WHERE 
    a27.Column1 = 'A27' 

Если таблицы больше или производительность может быть проблемой, вы можете создать временную таблицу и сделать это таким образом.

IF OBJECT_ID('tempdb..#Combined') IS NOT NULL 
    BEGIN 
     DROP TABLE #Combined 
    END 

CREATE TABLE #Combined (
    TableName CHAR(3) 
    ,Column2 BIGINT 
) 

INSERT INTO #Combined (TableName, Column2) 
SELECT Column1, Column2 
FROM 
    @A27 

INSERT INTO #Combined (TableName, Column2) 
SELECT Column1, Column2 
FROM 
    @A28 

;WITH cteRowNum AS (
    SELECT * ,ROW_NUMBER() OVER (ORDER BY Column2) AS RowNumber 
    FROM 
     #Combined 
) 

SELECT *, DENSE_RANK() OVER (ORDER BY a27.Column2) as GroupingId 
FROM 
    cteRowNum a27 
    LEFT JOIN cteRowNum a28 
    ON a28.RowNumber IN (a27.RowNumber + 1, a27.RowNumber + 2) 
    AND a28.TableName = 'A28' 
WHERE 
    a27.TableName = 'A27' 
+0

Спасибо, Мэтт, это очень помогает, я пытаюсь воспроизвести то, что вы сделали, но значения A28 и A27 находятся в их собственных таблицах соответственно, поэтому я не могу получить правильные результаты, которые вы имеете при запросе двух столы. Как бы вы это сделали? – abs786123

+0

@ abs786123 Вы можете использовать объединение всех и объединить таблицы или временную таблицу. Я обновил ответ. Если ответ удовлетворяет ваши потребности, отметьте его как принятый, чтобы другие знали, что у вас есть решение, и очки репутации присуждаются. Спасибо – Matt

+0

Еще раз спасибо Matt, что, если столбцы в таблице a27 и a28 имеют разные типы данных в столбцах, которые должны быть показаны. Каким-либо образом обойти это, поскольку два выше будут зависеть от того, какие типы данных будут одинаковыми в наборах результатов? – abs786123

1

Лучшее предположение учитывая отсутствие форматированного вывода ...

  • Я использовал CTE (Common таблицы Expression) для набора данных и aa cte для генерации номеров строк для каждого набора.
  • Я использовал левые соединения, так как не знаю, будет ли всегда 3 строки.
  • Я предположил, что col3 был уникальным идентификатором для каждой группы/раздела.
  • Я предположил, что значение col2 всегда будет самым низким для A27.
  • Я использую ROW_NUMBER на номер строки для каждого раздела

.

WITH dataset as (
SELECT 'A27' as Col1,346467413 as col2,68723601 as col3 UNION ALL 
SELECT 'A28' as Col1,346467414 as col2,68723601 as col3 UNION ALL 
SELECT 'A28' as Col1,346467416 as col2,68723601 as col3 UNION ALL 
SELECT 'A27' as Col1,349454768 as col2,8908697809 as col3 UNION ALL 
SELECT 'A28' as Col1,349454769 as col2,8908697809 as col3 UNION ALL 
SELECT 'A28' as Col1,349454771 as col2,8908697809 as col3), 

CTE AS 
(SELECT col1, col2, col3, row_number() over (partition by col3 order by col2) rn 
FROM dataset) 

SELECT A.Col1, A.Col3, A.Col2, B.Col2 as, C.Col2 
FROM cte A 
LEFT JOIN cte B 
on A.Col3 = B.Col3 and A.Rn= 1 and b.rn = 2 
LEFT JOIn cte C 
on A.Col3 = C.Col3 and A.RN =1 and c.RN = 3 

Это должно привести к плоскому выходу, который затем может быть помещен в отдельные таблицы по мере необходимости.

Смежные вопросы