Цикл SQL Server по строкам для формирования групп - PullRequest
0 голосов
/ 20 сентября 2018

Я использую SQL Server 2008 R2 / 2014. Я хочу найти запрос SQL, который может выполнять следующие действия:

Правила:

  1. Каждая [группа] должна иметь [Число] 1 до 6, чтобы быть полной группой.
  2. [Имя] в каждой [группе] должно быть уникальным.
  3. Каждая строка может использоваться только 1 раз.

Таблица перед сортировкой ...

Name   Number    Group  
----   ------    -----
A        1  
B        6  
A      123  
C        3  
B        4  
C       23  
D       45  
D        4  
C       56  
A       12  
D       56  

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

Name  Number  Group  
----  ------  ----- 
A       1       1  
C      23       1  
D      45       1  
B       6       1  

A     123       2  
D       4       2  
C      56       2  

A      12       3  
C       3       3  
B       4       3  
D      56       3

Я пытался найти подгруппу, в которой [Number] состоит из 1-6 сметод конкатенации ниже ...

SELECT *
FROM [Table1] ST2
WHERE 
    SUBSTRING((SELECT ST1.[Number] AS [text()]
               FROM [Table1] ST1
               -- WHERE ST1.[Group] = ST2.[Group]
               ORDER BY LEFT(ST1.[Number],1)           
               FOR XML PATH ('')), 1, 1000) = '123456' 

Ответы [ 2 ]

0 голосов
/ 21 сентября 2018

Я не уверен, можно ли это сделать проще или нет, но вот мой путь к этому ... Расширенное предупреждение, для этого требуются некоторые средства разбиения строк.Поскольку вы не в 2016 году, я включил функцию в начало сценария.

Основная часть работы представляет собой рекурсивный CTE, который строит столбцы Name и Number в группы с разделителями-запятыми.Затем мы сокращаем наш рабочий набор до тех групп, в которых числа будут создавать 123456 , разделяем группы и используем ROW_NUMBER() OVER... для их идентификации, а затем выбираем на основе новых данных.

Демо: http://rextester.com/NEXG53500

CREATE FUNCTION [dbo].[SplitStrings]
(
   @List       NVARCHAR(MAX),
   @Delimiter  NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
   RETURN 
   (  
      SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
      FROM 
      ( 
        SELECT x = CONVERT(XML, '<i>' 
          + REPLACE(@List, @Delimiter, '</i><i>') 
          + '</i>').query('.')
      ) AS a CROSS APPLY x.nodes('i') AS y(i)
   );

GO


CREATE TABLE #temp
(
name VARCHAR(MAX),
number INT
)

INSERT INTO #temp
VALUES
('a',1),
('b',6),
('a',123),
('c',3),
('b',4),
('c',23),
('d',45),
('d',4),
('c',56),
('a',12),
('d',56);

/*** Recursively build groups based on information from #temp ***/
WITH groupFinder AS
(
    SELECT CAST(name AS VARCHAR(MAX)) AS [groupNames], CAST(number AS VARCHAR(max)) AS [groupNumbers] FROM #temp
    UNION ALL
    SELECT
        cast(CONCAT(t.[Name],',',g.[groupNames]) as VARCHAR(MAX)), 
        CAST(CONCAT(CAST(t.[Number] AS VARCHAR(max)),',',CAST(g.[groupNumbers] AS VARCHAR(max))) AS VARCHAR(max))
    FROM #temp t
        JOIN groupFinder g
    ON 
        g.groupNames NOT LIKE '%' + t.name+'%'
        AND g.[groupNumbers] NOT LIKE '%' + CAST(t.number/100 AS VARCHAR(10)) +'%'
        AND g.[groupNumbers] NOT LIKE '%' + CAST(t.number/10 AS VARCHAR(10)) +'%'
        AND g.[groupNumbers] NOT LIKE '%' + CAST(t.number%10 AS VARCHAR(10)) +'%'
)
/*** only get groups where the numbers form 123456 ***/
, groupPruner AS
( 
    SELECT *, ROW_NUMBER() OVER (ORDER BY [groupNames]) AS [rn] FROM groupFinder WHERE REPLACE([groupNumbers],',','') = '123456'
)
/*** split the name group and give it identifiers ***/
, nameIdentifier AS
(
    SELECT g.*, c1.[item] AS [Name], ROW_NUMBER() OVER (PARTITION BY [rn] ORDER BY (SELECT NULL)) AS [rn1]
    FROM groupPruner g
    CROSS APPLY splitstrings(g.groupnames,',') c1
)
/*** split the number group and give it identifiers ***/
, numberIdentifier AS
(
    SELECT g.*, c1.[item] AS [Number], ROW_NUMBER() OVER (PARTITION BY [rn], [rn1] ORDER BY (SELECT NULL)) AS [rn2]
    FROM nameIdentifier g
    CROSS APPLY splitstrings(g.groupNumbers,',') c1
)
SELECT [Name], [Number], [rn] AS [Group]
    --,groupnames, groupNumbers /*uncomment this line to see the groups that were built*/
FROM numberIdentifier
    WHERE rn1 = rn2 
ORDER BY rn, rn1


DROP TABLE #temp
0 голосов
/ 21 сентября 2018

Может быть, вам следует проверить функцию ROW_NUMBER.

select Name
     , Number
     , ROW_NUMBER () OVER(PARTITION BY Name ORDER BY Number) as Group 
from [Table1]

Если у вас более 6 строк с одинаковым значением NAME, тогда будет возвращено больше групп.Вы можете отфильтровать дополнительные группы, поскольку вас интересуют только 6 групп с уникальными значениями столбца NAME.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...