2011-10-04 3 views
1

나는 다음과 같이 SQL 서버 테이블 (이미 정렬)가 :이전 행을 검사하는 SQL 쿼리를 작성하는 방법은 무엇입니까?

stringname 
========== 
stringA 
stringA 
stringB 
stringB 
stringA 
stringC 
stringC 
stringC 
stringA 

나는 다음과 같은 출력을 싶습니다

원래 테이블과 각 문자열 이름 들면,
stringname previous_stringname count 
========== =================== ===== 
stringA NULL    1 
stringA stringA    1 
stringA stringB    1 
stringA stringC    1 
stringB stringA    1 
stringB stringB    1 
stringC stringA    1 
stringC stringC    2 

그 각 stringname의 이전 항목, 나는 이전의 각 문자열 (첫 번째 항목에 대해 NULL)을 가진 횟수로 출력하고 싶습니다.

어떻게 SQL 쿼리를 작성할 수 있습니까?

내가 folllowing이 트릭을 할 것입니다,하지만 답변에 대한 대가로 내가 구글 "재귀"당신을 원하고 구글이 :)

를 검색 할 제안을 확인 SQL 서버를 2008

+0

나는 이것을 SQL에서 쉽게 할 수 있을지 확신하지 못합니다. 데이터를 쿼리하는 것이 더 낫고 프로그램이 로직을 처리하게하십시오. –

+0

ROW_NUMBER()을 (를)보고 셀프 합류 –

+0

ROW_NUMBER는 (는)에 의해 상수를 사용할 수 없기 때문에 도움이되지 않습니다. 'IDENTITY' 컬럼과 같이 테이블에 다른 식별자가 필요하다. 문자열 이름이 당신이 원하는 순서대로 질의되도록하기 위해서이다. – Wil

답변

2

재귀 필요는 없다; 그냥 다음을 사용하십시오 :

select b.stringname as stringname, a.stringname as previous_stringname 
    into #tmp 
    from (select stringname, row_number() over (order by id /* $/0 */) as row from testing) a 
    right outer join (select stringname, row_number() over (order by id /* $/0 */) as row from testing) b 
    on a.row = b.row - 1; 
select *, count(*) as [count] from #tmp group by stringname, previous_stringname; 
1

을 사용하고 있습니다 설명 : 재귀는 행 1에서 임시 테이블과 CTE 사이의 행에 참여하여 발생합니다. 이 방법은 독립적 인 IDENTITY 열 (이 경우 ID)을 사용하는 것에 따라 달라지며 ROWNUMBER()을 사용하여 ID의 가능한 틈을 설명합니다. ROW_NUMBER()JOIN에서 사용할 수 없기 때문에 CTE의 재귀 부분에서 하위 쿼리를 사용해야했습니다. 비록 당신이 연속 ID를 가지고있다하더라도, 어쨌든 틈새가 엉망이되기 때문에 안전을 위해서 ROW_NUMBER을 사용하는 것이 좋습니다.

CREATE TABLE #tmp (id INT IDENTITY(1,1),stringname NVARCHAR(MAX)) 

INSERT #tmp (stringname) 

VALUES 
('stringA') 
,('stringA') 
,('stringB') 
,('stringB') 
,('stringA') 
,('stringC') 
,('stringC') 
,('stringC') 
,('stringA') 

;WITH StringNames 
AS(
SELECT 
    ROW_NUMBER() OVER (ORDER BY ID) AS Row --Accounts for gaps in ID 
    ,stringname 
    ,CAST(NULL AS NVARCHAR(MAX)) AS previous_stringname  
FROM #tmp 
WHERE id = 1 
UNION ALL 
SELECT t.Row 
    ,t.stringname 
    ,s.stringname AS previous_stringname 
    FROM (
      SELECT 
      ROW_NUMBER() OVER (ORDER BY ID) AS Row --Accounts for gaps in ID 
      ,stringname 
      FROM #tmp) AS t 
JOIN StringNames AS s ON t.row - 1 = s.row 
) 

SELECT 
    DISTINCT 
    stringname 
    ,previous_stringname 
    ,COUNT(*) AS count 
FROM StringNames 
GROUP BY 
    stringname 
    ,previous_stringname 
ORDER BY stringname 
0

@Wil 왜 ID 열이 필요합니까?

데이터

CREATE TABLE #table (stringname NVARCHAR(MAX)) 

INSERT #table (stringname) 
VALUES ('stringA') 
     ,('stringA') 
     ,('stringB') 
     ,('stringB') 
     ,('stringA') 
     ,('stringC') 
     ,('stringC') 
     ,('stringC') 
     ,('stringA') 

쿼리

;WITH [cteRowNumbers] AS (
    SELECT ROW_NUMBER() OVER (ORDER BY $/0) AS [RowNumber], 
      [stringname], 
      CAST(NULL AS NVARCHAR(MAX)) AS [previous_stringname] 
    FROM #table 
) 
,[cteStringNames] AS (
    SELECT [RowNumber], 
      [stringname], 
      [previous_stringname] 
    FROM (
     SELECT TOP 1 
       [RowNumber], 
       [stringname], 
       [previous_stringname] 
     FROM [cteRowNumbers] 
    ) t 
    UNION ALL 
    SELECT t.[RowNumber], 
      t.[stringname], 
      s.[stringname] AS [previous_stringname] 
    FROM [cteRowNumbers] AS t 
    INNER JOIN [cteStringNames] AS s 
     ON t.[RowNumber] - 1 = s.[RowNumber] 
) 

SELECT [stringname], 
     [previous_stringname], 
     COUNT(*) AS [count] 
FROM [cteStringNames] 
GROUP BY stringname, previous_stringname 
ORDER BY stringname 
+0

나는 그 트릭을 본적이 없다. 그것은 나에게 새로운 것이다. :) 그럴 경우, 식별 컬럼은 필요하지 않을 것이다. 물론 하나 또는 두 개의 CTE가 결과를 가져 오는 데 사용되는지 여부는 디자인 기본 설정이됩니다. – Wil

관련 문제