2016-12-21 5 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

어떤 아이디어 : F 우리는 하나 둘 중 하나가 제대로 반환 조인 제거? 대단히 감사합니다.

다음은 최종 해결책입니다. 건배!

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의 카디널리티를 (각 행은 한 번만 나타납니다)

당신은 PK에 가입하고 있기 때문에, 샘플 데이터는 X가 Qty_CTE X CPT_CTE가 numrow 1 1x1x1 레코드에 대한 (1x2x2 기록을 수행하는 5 개 레코드를 반환해야 numrow 2). Qty_CTE 또는 CPT_CTE을 제거하면 3 레코드를 반환해야합니다 (numrow 2의 경우 1x1x1 레코드, numrow 1의 경우 1x2 레코드).

예상되는 결과를 기반으로 솔루션을 제안 할 수 있습니다.

+0

나는 당신이 정확하다고 생각하지만, 왜 이런 일이 발생합니까? sampleData x Qty_CTE x CPT_CTE? –

+0

간단한 세트 이론. 간단히 말해 각 테이블은 WHERE 필터에 따라 차례로 계산되어 하나의 * 결과 세트 *가됩니다. 그런 다음 이러한 결과 집합이 서로 결합됩니다. INNER JOIN의 경우, 조합은 * intersection *이므로 위에서 언급 한 것으로 끝납니다. – Sebas

+0

집합 A와 집합 B가 교차하지 않고 조건없이 A x B 레코드 집합이됩니다. 미안하지만 조금 설명하기가 어색 할 지 모르겠지만 실현할 필요가 없다는 것을 깨달았습니다. 연습해야합니다 – Sebas