2011-10-04 5 views
1

J'ai la table (déjà triée) dans le serveur SQL avec les éléments suivants:Comment écrire une requête SQL qui examine la ligne précédente?

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

je voudrais génèrerait les éléments suivants:

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 

C'est, pour chaque NomChaîne dans la table d'origine et Chacune de l'entrée précédente de ce stringname, je voudrais le sortir avec le nombre de fois qu'il avait chaque chaîne précédente (avec NULL pour la première entrée).

Comment écrire une requête SQL pour cela?

J'utilise SQL Server 2008.

+0

Je ne suis pas sûr que vous serez en mesure de le faire facilement en SQL. Il vaut mieux interroger les données et laisser le programme gérer la logique. –

+0

recherchez ROW_NUMBER() et une jointure automatique –

+0

ROW_NUMBER n'aidera pas car vous ne pouvez pas utiliser une constante dans un ordre par. Vous avez besoin d'un autre identificateur dans la table comme une colonne 'IDENTITY' afin de s'assurer que les noms de chaîne sont interrogés dans l'ordre que vous voulez. – Wil

Répondre

2

récursivité est pas nécessaire; il suffit d'utiliser:

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

Le folllowing fera l'affaire, mais en échange de la réponse que je veux que vous Google « récursivité » et voir ce que Google propose de rechercher :)

Clarification: La récursion se fait en se joignant à la rangée - 1 à ranger entre la table temporaire et le CTE. Cette méthode dépend de l'existence d'une colonne IDENTITY indépendante (ID dans ce cas) et utilise ROWNUMBER() pour tenir compte des éventuelles lacunes dans les ID. Depuis ROW_NUMBER() ne peut pas être utilisé dans un JOIN J'ai dû recourir à l'utilisation d'une sous-requête dans la partie récursive du CTE. Même si vous savez que vous avez des IDs continus, je vous recommande d'utiliser le ROW_NUMBER pour ce type de requête de toute façon juste pour être sûr, car les lacunes vont le gâcher.

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, pourquoi avez-vous besoin de la colonne d'identité?

données

CREATE TABLE #table (stringname NVARCHAR(MAX)) 

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

Recherche

;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

Je n'ai jamais vu cette astuce avant, c'est une nouvelle sur moi :) Dans ce cas, alors la colonne d'identité ne serait pas nécessaire. Bien sûr, alors il devient une préférence de conception si un ou deux CTE sont utilisés pour obtenir les résultats. – Wil

Questions connexes