2016-12-21 3 views
2

Данные для этого проекта содержат два столбца с строками с разделителями с запятой. Это фактически упорядоченные пары. Так, например, в: «a; b; c», «x; y; z», «a» сопряжено с «x». Цель нашего запроса - создать таблицу, в которой эта взаимосвязь четко представлена ​​одной строкой за раз.SQL Server CTE не отвечает должным образом

Вот скрипт, чтобы заново создать образец данных:

DROP TABLE IF EXISTS dbo.sampleData; 
DROP TABLE IF EXISTS dbo.lookupCPT; 
GO 

CREATE TABLE sampleData 
(
    numRow bigint IDENTITY(1,1) NOT NULL CONSTRAINT PK_numRow PRIMARY KEY, 
    sDelimQty varchar(MAX) NULL, 
    sDelimCPT varchar(MAX) NULL 
) 

CREATE TABLE lookupCPT 
(
    numRow bigint IDENTITY(1,1) NOT NULL CONSTRAINT PK_numRowCPT PRIMARY KEY, 
    sCPTCode varchar(10) NULL, 
    decCPTRate decimal(16,2) NULL 
) 

INSERT [dbo].[lookupCPT] ([numRow], [sCPTCode], [decCPTRate]) 
VALUES (1, N'123', CAST(4.00 AS Decimal(16, 2))) 

INSERT [dbo].[lookupCPT] ([numRow], [sCPTCode], [decCPTRate]) 
VALUES (2, N'456', CAST(5.00 AS Decimal(16, 2))) 

INSERT [dbo].[lookupCPT] ([numRow], [sCPTCode], [decCPTRate]) 
VALUES (3, N'789', CAST(7.00 AS Decimal(16, 2))) 

INSERT [dbo].[sampleData] ([numRow], [sDelimQty], [sDelimCPT]) 
VALUES (1, N'1;2', N'123;789') 

INSERT [dbo].[sampleData] ([numRow], [sDelimQty], [sDelimCPT]) 
VALUES (2, N'3', N'456') 

Мы попытались осуществить это с помощью общих табличных выражений:

WITH Qty_CTE (numRowQ, Qty) AS 
(
    SELECT numRow, value 
    FROM sampleData 
    CROSS APPLY STRING_SPLIT(sDelimQty, ';') 
), 
CPT_CTE (numRowC, CPT) AS 
(
    SELECT numRow, value 
    FROM sampleData 
    CROSS APPLY STRING_SPLIT(sDelimCPT, ';') 
) 
SELECT * 
FROM sampleData 
JOIN CPT_CTE c on c.numRowC = sampleData.numRow 
JOIN Qty_CTE q on q.numRowQ = sampleData.numRow 

Однако, делая это удваивает количество строк в наш выход:

q1

Но я е убираем либо один из двух соединений, она возвращает правильно:

q2

Любые идеи? Большое спасибо

После всех полезных ответов ниже приведено окончательное решение. Ура!

WITH Qty_CTE (numRowQ, Qty, RN) AS 
(
    SELECT 
     numRow, value, 
     ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS RN 
    FROM 
     sampleData 
    CROSS APPLY 
     STRING_SPLIT(sDelimQty, ';') 
), 
CPT_CTE (numRowC, CPT, CPTRate, RN) AS 
(
    SELECT 
     s.numRow, value as CPT, l.decCPTRate as CPTRate, 
     ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS RN 
    FROM 
     sampleData s 
    CROSS APPLY 
     STRING_SPLIT(sDelimCPT, ';') 
    JOIN 
     lookupCPT l ON value = l.sCPTCode 
) 
SELECT 
    numRow, sDelimCPT, sDelimQty, CPT, CPTRate, Qty, CPTRate * Qty as Total 
FROM 
    sampleData 
JOIN 
    CPT_CTE c on c.numRowC = sampleData.numRow 
JOIN 
    Qty_CTE q on q.numRowQ = sampleData.numRow AND c.RN = q.RN 
+0

- это разграниченные числа * всегда * в порядке возрастания? –

+0

нет гарантии, к сожалению: - \ –

+0

для меня, q1 логичен, а также q2. – Sebas

ответ

0

Если ваш STRING_SPLIT функция сохраняет заказ, тогда это сработает.

WITH Qty_CTE (numRowQ, Qty, RN) AS 
(
SELECT numRow, value, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS RN 
FROM sampleData 
    CROSS APPLY STRING_SPLIT(sDelimQty, ';') 
), 
CPT_CTE (numRowC, CPT, RN) AS 
(
SELECT numRow, value, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS RN 
FROM sampleData 
    CROSS APPLY STRING_SPLIT(sDelimCPT, ';') 
) 
SELECT * FROM sampleData 
JOIN CPT_CTE c on c.numRowC = sampleData.numRow 
JOIN Qty_CTE q on q.numRowQ = sampleData.numRow AND c.RN = q.RN 
+0

Извините, но функция STRING_SPLIT фактически была частью выпуска SQL Server 2016. Кроме того, я пробовал это, и ORDER BY SELECT NULL не может быть интерпретирован. –

+0

Теперь он должен работать (я забыл круглые скобки вокруг SELECT NULL). О STRING_SPLIT либо создайте его самостоятельно, либо выполните руководство по STRING_SPLIT, чтобы узнать, гарантировано ли оно. – DVT

+0

Genius !! Это правильный ответ. СПАСИБО БОЛЬШОЕ!!! –

0

Оба примера q1 и q2 работают должным образом. Qty_CTE имеет мощность 3 (запись 1 появляется дважды, запись 2 появляется один раз), то есть CPT_CTE. sampleData имеет мощность 2 (каждая строка появляется только один раз)

Поскольку вы присоединяетесь на ПК, SampleData х Qty_CTE х CPT_CTE должен вернуть 5 записей, которые он делает (1x2x2 записи для numrow 1 и 1x1x1 записи для numrow 2). Если вы удалите либо Qty_CTE, либо CPT_CTE, он должен вернуть 3 записи, которые он делает (запись 1x1x1 для записей 2 и 1x2 для номера 1).

Мы могли бы предложить решение на основе ожидаемого результата, если бы у вас его было.

+0

Я считаю, что вы правы, но почему это происходит: sampleData x Qty_CTE x CPT_CTE? –

+0

Простая теория множеств. Проще говоря, каждая таблица будет вычисляться в соответствии с вашими WHERE-фильтрами, в результате получается один * resultet *. Затем эти результаты будут объединены друг с другом. В случае INNER JOIN сочетание * * пересечение *, поэтому заканчивается тем, что я упоминал выше. – Sebas

+0

Набор A, пересеченный с множеством B без каких-либо условий, приведет к набору записей A x B. Извините, если я немного неуклюжий, объясняя это, но понимаю, что мне никогда не приходилось это делать :) Я должен попрактиковаться – Sebas

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