【问题标题】:Fill missing ID with dragged data in SQL Server table用 SQL Server 表中的拖动数据填充缺失的 ID
【发布时间】:2017-03-03 17:47:53
【问题描述】:

我正在尝试解决这个问题;我有一张这样的桌子:

SELECT [ID], [STRING] FROM Test

ID      STRING
38      Hi 38
39      Hi 39
42      Hi 42
46      Hi 46
47      Hi 47
49      Hi 49

TDL:

创建表#Test ([ID] int, [STRING] varchar(50)) ;

INSERT INTO #Test
    ([ID], [STRING])
VALUES
    (38, 'Hi 38'),
    (39, 'Hi 39'),
    (42, 'Hi 42'),
    (46, 'Hi 46'),
    (47, 'Hi 47'),
    (49, 'Hi 49')
;

IDINTSTRINGVARCHAR(50),我应该编写一个新的 SELECT 查询来获得这个结果:

ID      STRING
38      Hi 38
39      Hi 39
40      Hi 39
41      Hi 39
42      Hi 42
43      Hi 42
44      Hi 42
45      Hi 42
46      Hi 46
47      Hi 47
48      Hi 47
49      Hi 49

填充渐进式 ID 并将之前的 STRING 值用于新 ID。

我尝试过 CTE,但没有结果。

【问题讨论】:

  • 在发布此类问题时,将您的尝试包含在有效查询中会很有帮助。尽管不完整,但我们的努力得到了认可和赞赏。

标签: sql sql-server sql-server-2008 common-table-expression


【解决方案1】:

在这里,我通过将差异复制到多个行来生成差异行,将它们转换为 xml 并与实际表外应用。

DECLARE @Table TABLE
    (
      ID INT ,
      STRING VARCHAR(50)
    )
INSERT  INTO @Table
VALUES  ( 38, 'Hi 38' )
,       ( 39, 'Hi 39' )
,       ( 42, 'Hi 42' )
,       ( 46, 'Hi 46' )
,       ( 47, 'Hi 47' )
,       ( 49, 'Hi 49' );
WITH    cte
          AS ( SELECT   ID ,
                        STRING ,
                        CAST(REPLICATE('<k>1</k>',
                                       LEAD(ID) OVER ( ORDER BY ID ) - ID) AS XML) AS x
               FROM     @Table
             ),
        cte2
          AS ( SELECT   a.ID ,
                        a.STRING ,
                        ROW_NUMBER() OVER ( PARTITION BY a.ID ORDER BY ID )
                        - 1 AS ID2
               FROM     cte AS a
                        OUTER APPLY x.nodes('/k') t ( x )
             )
    SELECT  ID + ID2 AS ID ,
            STRING
    FROM    cte2 AS a
    ORDER BY a.ID

【讨论】:

  • 感谢您的回答,但我忘了说明我使用的是 SLQ Server 2008(未定义 LEAD 函数)。
【解决方案2】:

对于 SQL Server 2008

;WITH    id_rn ( id, string, rn )
          AS ( SELECT   id ,
                        string ,
                        ROW_NUMBER() OVER ( ORDER BY id )
               FROM     t
             ),
        id_min_max ( min_id, max_id )
          AS ( SELECT   MIN(id) ,
                        MAX(id)
               FROM     t
             ),
        rows ( n )
          AS ( SELECT   min_id
               FROM     id_min_max
               UNION ALL
               SELECT   n + 1
               FROM     rows
               WHERE    n < ( SELECT    max_id
                              FROM      id_min_max
                            )
             )
    SELECT  COALESCE(r.n, i1.id) AS id ,
            i1.string AS string
    FROM    id_rn AS i1
            LEFT JOIN id_rn AS i2 ON i2.rn = i1.rn + 1
            LEFT JOIN rows AS r ON r.n BETWEEN i1.id AND i2.id - 1
    ORDER BY id
OPTION  ( MAXRECURSION 0 );

;WITH    id_occ ( id, string, #id_missing_occ )
          AS ( SELECT   id ,
                        string ,
                        LEAD(id) OVER ( ORDER BY id ) - id - 1
               FROM     t
             ),
        max_id_occ ( #max_generated_rows )
          AS ( SELECT   MAX(#id_missing_occ) + 1
               FROM     id_occ
             ),
        rows ( n )
          AS ( SELECT   1
               UNION ALL
               SELECT   n + 1
               FROM     rows
               WHERE    n < ( SELECT    #max_generated_rows
                              FROM      max_id_occ
                            )
             )
    SELECT  id + COALESCE(n - 1, 0) ,
            string
    FROM    id_occ AS i
            LEFT JOIN rows AS r ON r.n <= i.#id_missing_occ + 1
                                   AND #id_missing_occ > 0
OPTION  ( MAXRECURSION 0 );

【讨论】:

  • 感谢您的回答,但我忘了说明我使用的是 SLQ Server 2008(未定义 LEAD 函数)。
  • @user2595496,请查看添加的解决方案
  • @Tanner,请不要在没有先询问我的情况下编辑我的帖子。谢谢。
  • @DuduMarkovitz 我只是让它更具可读性,内容完全一样。这是为了社区而不是你的利益。如果您受到侮辱,我深表歉意,如果您对我的编辑感到冒犯,请随时将编辑回滚到不太漂亮的版本。
【解决方案3】:

您必须使用足够大的 Tally 表来覆盖表中找到的整个ID 值范围。使用来自this source 的 Tally 表,查询可能如下所示:

WITH Tally (n) AS
(
    -- 1000 rows
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
    FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
    CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
    CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
), Min_Max_CTE(min_id, max_id) AS
(
   SELECT MIN(ID) AS min_id, MAX(ID) AS max_id
   FROM mytable
)
SELECT ID, MAX(STRING) OVER (PARTITION BY grp) AS STRING
FROM (
   SELECT t1.n AS ID, t3.STRING,
          MAX(t3.ID) OVER (ORDER BY t1.n) AS grp
   FROM Tally As t1
   CROSS JOIN Min_Max_CTE AS t2
   LEFT JOIN mytable AS t3 ON t3.ID = t1.n
   WHERE t1.n BETWEEN t2.min_id AND t2.max_id) AS t

注意:上述查询适用于 SQL Server 2012+。

【讨论】:

  • 嗨,解决方案的概念似乎很好,除了行的生成。如果输入数据仅包含 ID 为 999999 和 1000000 的 2 行,您将生成 1M 条记录,而实际上没有使用任何记录,您还必须更改查询以支持此行数。
  • @DuduMarkovitz 是的,这是正确的。该查询最适合ID 值相对较小的情况。
【解决方案4】:

试试这个:

    WITH compteur(id, minval, maxval) AS
    (
        SELECT (SELECT min(id) FROM test) , (SELECT min(id) FROM test) , (SELECT max(id)  FROM test) 
        UNION ALL
        SELECT id+1, minval, maxval FROM compteur 
        WHERE id < maxval
    )
    SELECT f1.id, f3.string FROM compteur f1
    OUTER APPLY
            (
            SELECT top 1 * FROM test f2
            WHERE f1.id>=f2.id
            ORDER BY f2.id DESC

            ) f3 
    OPTION (MAXRECURSION 1000);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-11
    相关资源
    最近更新 更多