SQL ID的每个组合

时间:2016-06-15 09:04:33

标签: sql sql-server

相当棘手的情况。我有一张桌子如下。基本上我想从SQL Server 2012中的每个RangeSet中获取范围的所有组合。

最好我展示一个结构和所需输出的例子。问题是RangeSetID的数量可以是动态的,并且RangeID的数量在每个范围集中都是动态的

RangeID RangeSetID
------------------
1          4
2          4
3          4
4          4
5          2
6          2
7          2
8          2
9          2
10         2
11         1
12         1
13         1
14         1
15         1
16         1
17         3
18         3
19         3
20         3

我需要输出以递归方式创建以下数据集:

1 5 11 17  (first from range4, first from range2, first from range1, first from range3)
1 5 11 18 (first from range4, first from range2, first from range1, second from range3)
1 5 11 19 (first from range4, first from range2, first from range1, third from range3)
1 5 11 20 (first from range4, first from range2, first from range1, fourth from range3)
1 5 12 17 (first from range4, first from range2, second from range1, first from range3)
1 5 12 18 (first from range4, first from range2, second from range1, second from range3)
1 5 12 19
1 5 12 20

依此类推,直到我从每个RangeSetID到达最后一个RangeID并导致

4 10 16 20 (last from range4, last from range2, last from range1, last from range3)

哪个最终会产生以下结果,其中RateID 1垂直显示第一个结果,以允许RangeSetID的动态数量

RateID    RangeID
------------------
  1         1
  1         5
  1         11
  1         17
  2         1
  2         5
  2         11
  2         18

这应该导致11,000行(大约)。我曾尝试过CROSS JOIN等,但我根本无法解决这个问题。

那里有天才吗?

由于

3 个答案:

答案 0 :(得分:0)

这种作品,但它过于复杂,可以做一些调整。请注意,我更改了您的示例数据,以包含间隙以测试其是否正常工作。

DECLARE @table TABLE (
    range_id INT,
    range_set_id INT);
INSERT INTO @table SELECT 1, 4;
INSERT INTO @table SELECT 2, 4;
INSERT INTO @table SELECT 3, 4;
INSERT INTO @table SELECT 5, 4;
INSERT INTO @table SELECT 8, 2;
INSERT INTO @table SELECT 10, 2;
INSERT INTO @table SELECT 17, 2;
INSERT INTO @table SELECT 18, 2;
INSERT INTO @table SELECT 19, 2;
INSERT INTO @table SELECT 20, 2;
INSERT INTO @table SELECT 21, 1;
INSERT INTO @table SELECT 23, 1;
INSERT INTO @table SELECT 28, 1;
INSERT INTO @table SELECT 29, 1;
INSERT INTO @table SELECT 30, 1;
INSERT INTO @table SELECT 33, 1;
INSERT INTO @table SELECT 35, 3;
INSERT INTO @table SELECT 38, 3;
INSERT INTO @table SELECT 39, 3;
INSERT INTO @table SELECT 40, 3;

--Work out the order of the range_set_ids
WITH ordered AS (
    SELECT
        range_set_id,
        range_id,
        ROW_NUMBER() OVER (PARTITION BY range_set_id ORDER BY range_id) AS sequential_id
    FROM
        @table),
ranges AS (
    SELECT
        range_set_id,
        MIN(range_id) AS range_id
    FROM
        @table
    GROUP BY
        range_set_id),
range_order AS (
    SELECT
        range_set_id,
        ROW_NUMBER() OVER (ORDER BY range_id) AS order_id
    FROM
        ranges),
set_count AS (
    SELECT
        MAX(order_id) AS max_order_id
    FROM
        range_order),
start_and_end AS (
    SELECT
        o.range_set_id,
        o.order_id,
        MIN(range_id) AS min_range_id,
        MAX(range_id) AS max_range_id,
        COUNT(range_id) AS iterations
    FROM
        range_order o
        INNER JOIN @table t ON t.range_set_id = o.range_set_id
    GROUP BY
        o.range_set_id,
        o.order_id),
toggles AS (
    SELECT
        s.range_set_id,
        s.order_id,
        s.iterations AS toggle
    FROM
        start_and_end s
        CROSS JOIN set_count c
    WHERE
        s.order_id = c.max_order_id
    UNION ALL
    SELECT
        s.range_set_id,
        s.order_id,
        t.toggle * (s.iterations) AS toggle
    FROM
        toggles t
        INNER JOIN start_and_end s ON s.order_id = t.order_id - 1
    WHERE
        s.order_id > 0),
toggle_count AS (
    SELECT 
        MAX(toggle * s.iterations) AS max_toggle 
    FROM 
        toggles t
        CROSS JOIN set_count c
        INNER JOIN start_and_end s ON s.order_id = c.max_order_id),
all_combos AS (
    SELECT
        1 AS rate_set_id,
        o.range_set_id,
        1 AS sequential_id,
        o.order_id,
        lt.toggle AS reset_toggle,
        ISNULL(t.toggle, 1) AS increment_toggle,
        1 AS current_toggle
    FROM
        range_order o
        CROSS JOIN set_count c
        INNER JOIN toggles lt ON lt.order_id = o.order_id
        LEFT JOIN toggles t ON t.order_id = o.order_id + 1
    UNION ALL
    SELECT
        a.rate_set_id + 1,
        a.range_set_id,
        CASE
            WHEN a.current_toggle = a.reset_toggle THEN 1 --flip back at the end
            WHEN a.current_toggle % a.increment_toggle != 0 THEN a.sequential_id --something lower is still toggling
            ELSE a.sequential_id + 1 --toggle
        END,
        a.order_id,
        a.reset_toggle,
        a.increment_toggle,
        CASE
            WHEN a.current_toggle < a.reset_toggle THEN a.current_toggle + 1
            ELSE 1
        END
    FROM
        all_combos a
        CROSS JOIN set_count sc
        CROSS JOIN toggle_count tc
    WHERE
        a.rate_set_id < tc.max_toggle)
SELECT
    a.rate_set_id,
    a.range_set_id,
    o.range_id
FROM
    all_combos a
    INNER JOIN ordered o ON o.range_set_id = a.range_set_id AND o.sequential_id = a.sequential_id
ORDER BY
    a.rate_set_id,
    a.order_id
OPTION (MAXRECURSION 0);

答案 1 :(得分:0)

猜猜这应该有所帮助。快乐的编码!

;WITH CTE
AS
  (
  SELECT * FROM (
  SELECT ROW_NUMBER() over (order by [RangeID1] , [RangeID2], [RangeID3], [RangeID4]) as 'RateID', [RangeID1] , [RangeID2], [RangeID3], [RangeID4]  FROM 
  (
   select A.RangeID as [RangeID1], B.RangeID as [RangeID2], C.RangeID as [RangeID3], D.RangeID as [RangeID4]
    from [Range] as A
    inner join [Range] as B on (A.RangeID <= B.RangeID)
    inner join [Range] as C on (B.RangeID <= C.RangeID)
    inner join [Range] as D on (C.RangeID <= D.RangeID)
    where A.RangeSetID <> B.RangeSetID
    and B.RangeSetID <> C.RangeSetID
    and C.RangeSetID <> D.RangeSetID
  ) as A) T
  UNPIVOT ( RangeID FOR N IN ([RangeID1] , [RangeID2], [RangeID3], [RangeID4] ))P
  )
 SELECT RateID, RangeID
 FROM CTE

答案 2 :(得分:0)

在动态查询中实现了相同的逻辑。这应该适合你,我想

 declare @i int = 1;
 declare @count int = 0;
 declare @cols varchar(max) = '';
 declare @select varchar(max) = 'select ';
 declare @join varchar(max);
 declare @where varchar(max);
 declare @query varchar(max);

 declare @range varchar(100);
 declare @prevrange varchar(100);
 declare @rangeid varchar(100);

 select @count =count(distinct RangeSetID) from [Range];

 while @count > 0
 begin

     set @range = 'Range' + cast(@i as varchar(max));
     set @rangeid = 'RangeID' + cast(@i as varchar(max));

     set @cols = @cols + @rangeid + ', ';
     set @select = @select + @range + '.RangeID as '+@rangeid + ', ';

      if @i = 1
        begin
            set @join = ' from [Range] as ' + @range;
            set @where = 'where ' + @range + '.RangeSetID <> ';
        end
      else 
        begin
            set @prevrange = 'Range' + cast((@i - 1) as varchar(max));
            set @join = @join + ' inner join [Range] as ' + @range + ' on (' + @prevrange + '.RangeID <= ' + @range + '.RangeID)';
            if(@count = 1)
                set @where = @where + @range+ '.RangeSetID';
            else 
                set @where = @where + @range+ '.RangeSetID and '+ @range+ '.RangeSetID <> ';
        end

     set @i = @i + 1;
     set @count = @count - 1;
 end 

 set @query = ' 
;WITH CTE
AS
  (
  SELECT * FROM (
  SELECT ROW_NUMBER() over (order by '+ SUBSTRING(@cols, 0, LEN(@cols)) + ') as ''RateID'', '+ SUBSTRING(@cols, 0, LEN(@cols)) +'  FROM 
  (
    ' + SUBSTRING(@select, 0, LEN(@select)) + char(13) + @join + char(13) + @where + '
  ) as A) T
  UNPIVOT ( RangeID FOR N IN ('+(SUBSTRING(@cols, 0, LEN(@cols))) +' ))P
  )
 SELECT RateID, RangeID
 FROM CTE
 ';

 exec (@query);