【问题标题】:Incrementing sequence number on SQL insert在 SQL 插入时递增序列号
【发布时间】:2013-03-09 22:35:40
【问题描述】:

有没有使用 SQL Server 2005 插入多行并增加特定字段的简单方法?

注意,我不是在寻找涉及 identity 列的解决方案 - 请参阅问题底部以获取解释

(以下架构和数据已复制here in this SQLFiddle。)

例如,考虑下表和数据...

CREATE TABLE #TEMPTABLE (
   [PKID] INT IDENTITY, [FKID] INT, [MYTEXT] VARCHAR(10), [SEQUENCE] INT
)
INSERT INTO #TEMPTABLE ([FKID], [MYTEXT], [SEQUENCE]) VALUES (1, 'one', 1)
INSERT INTO #TEMPTABLE ([FKID], [MYTEXT], [SEQUENCE]) VALUES (1, 'two', 2)

-- Table data
PKID  FKID  MYTEXT  SEQUENCE
1     1     one     1
2     1     two     2

还有下面要插入的数据……

DECLARE @FKID INT
SET @FKID = 1
DECLARE @NEWDATA XML
SET @NEWDATA = '<data><text>three</text><text>four</text></data>'

以下内容可以写成SEQUENCE 字段显示为1,2,3,4 而不是当前的1,2,3,3

INSERT INTO #TEMPTABLE ([FKID], [MYTEXT], [SEQUENCE])
SELECT @FKID, 
       X.value('.','VARCHAR(10)'),
       (SELECT ISNULL(MAX([SEQUENCE]),0)+1 FROM #TEMPTABLE WHERE [FKID]=@FKID)
FROM @NEWDATA.nodes('/data/text') AS X(X)

-- Actual result...
PKID  FKID  MYTEXT  SEQUENCE
1     1     one     1
2     1     two     2
3     1     three   3
4     1     four    3  <-- Issue

-- Required result...
PKID  FKID  MYTEXT  SEQUENCE
1     1     one     1
2     1     two     2
3     1     three   3
4     1     four    4

更新:

回应@marc_s 的评论...

Identity 将是 2005 年的最佳解决方案......迄今为止最好的解决方案 - 为什么您明确排除它并坚持推出自己的解决方案? (有导致重复等的所有风险......)

有问题的表将保存多组 SEQUENCE 值,每个“组”都基于 FKID 值...因此该表可以保存这些数据...

PKID  FKID  MYTEXT  SEQUENCE
1     1     one     1
2     1     two     2
3     1     three   3
4     1     four    4
5     2     ett     1
6     2     tva     2
7     2     tre     3

【问题讨论】:

  • 我相信任何简单的方法都需要 SQL Server 2012。
  • 是的,@Pieter,我相信这会更容易 - 不幸的是,我坚持使用 2005 年,因为这是我们支持的最低数据库,我们的许多客户仍在使用它
  • 因为,@marc_s,序列与外键列直接相关。 IE。 FKID=1 可以有多个行,SEQUENCE=1,2,3FKID=2 也可以有SEQUENCE=1,2。对不起,如果我没有在 OP 中说清楚
  • 有什么理由你不能使用变量来做到这一点?声明它,并从您的选择语句中增加它?把变量写在最后。
  • 抱歉@Random,我不完全理解如何您将使用变量...请随时为您的解决方案提供答案

标签: sql-server sql-server-2005 insert sequence


【解决方案1】:

我无法在 2005 年进行测试,但您应该可以使用 CTE 对事物进行编号;

DECLARE @FKID INT
SET @FKID = 1
DECLARE @NEWDATA XML
SET @NEWDATA = '<data><text>three</text><text>four</text><text>five</text></data>'

;WITH cte AS (SELECT @FKID FKID, X.value('.','VARCHAR(10)') a, 
                  ROW_NUMBER() OVER (ORDER BY X) r
             FROM @NEWDATA.nodes('/data/text') AS X(X))
INSERT INTO TEMPTABLE ([FKID], [MYTEXT], [SEQUENCE])
SELECT fkid, a,
  (SELECT ISNULL(MAX([SEQUENCE]),0)+r FROM TEMPTABLE WHERE [FKID]=cte.fkid)
FROM cte;

SELECT * FROM TEMPTABLE;

给出结果:

1    1    one     1
2    1    two     2
3    1    three   3
4    1    four    4
5    1    five    5

更新

如果查询将只插入一个 FKID,则以下简化版本也可以工作(突出显示对当前查询的必要更改):

INSERT INTO #TEMPTABLE ([FKID], [MYTEXT], [SEQUENCE])
SELECT @FKID, 
       X.value('.','VARCHAR(10)'),
       (SELECT ISNULL(MAX([SEQUENCE]),0)+1 FROM #TEMPTABLE WHERE [FKID]=@FKID)
        + ROW_NUMBER() OVER (ORDER BY (SELECT 1))
FROM @NEWDATA.nodes('/data/text') AS X(X)

ROW_NUMBERORDER BY 子句中(SELECT 1) 的目的是避免指定任何特定的顺序。如有必要,可以将其更改为其他内容(例如,X.value('.','VARCHAR(10)')。

【讨论】:

  • +1,尽管 CTE 是不必要的。只需从子查询中删除+1 并在子查询后添加+ROW_NUMBER() ... 就足够了,即SEQUENCE 列的表达式如下所示:(SELECT ISNULL(MAX([SEQUENCE]),0) FROM TEMPTABLE WHERE [FKID]=cte.fkid) + ROW_NUMBER() OVER (ORDER BY (SELECT 1))
  • 感谢 Joachim,您的回答绝对正确。但是@AndriyM,你一针见血,这正是我所需要的(尽管[FKID]=ctr.fkid 应该是[FKID]=@FKID)。如果你做出这个答案,我会很高兴地标记它。
  • @freefaller:顺便说一句,如果它真的与这个不同,我会发布我的答案,which works fine for me。我能看到的这个答案的唯一问题是,在测试时,Joachim 在@NEWDATA XML 中又添加了一项five,但忘记在发布的输出中反映这一点。我还认为,在这个使用 CTE 的特定解决方案中,[FKID]=cte.fkid[FKID]=@FKID 都可以很好地工作。但是,您可能希望及时修改 CTE 以为多个 FKID 生成行,[FKID]=cte.fkid 会更好。
  • 是的,我明白你在说什么@AndriyM - 但查询一次只会处理一个 FKID,所以只需使用 ROW_NUMBER() OVER (ORDER BY (SELECT 1)) 就可以满足我的需要,而无需使用 CTE。如果您对 Joachim 获得分数感到高兴,那么我也可以。谢谢你们的帮助
【解决方案2】:

怎么样

    INSERT INTO #TEMPTABLE ([FKID], [MYTEXT], [SEQUENCE])
    SELECT @FKID, 
               X.value('.','VARCHAR(10)'),
              (SELECT ISNULL(Count(*),0)+1 FROM #TEMPTABLE)
    FROM @NEWDATA.nodes('/data/text') AS X(X)

【讨论】:

  • 谢谢@Charles,但结果相同,两个插入行都使用了初始计数......所以你仍然会得到3 出现两次。此外,这不会考虑SEQUENCE1 不连续的(不太可能,但仍然可能)情况
【解决方案3】:

试试这个:

with data as (
  select * from ( values
    (1,1,'one'),
    (2,1,'two'),
    (3,1,'three'),
    (5,1,'five'),
    (6,1,'six')
  ) data(PKID, FKID, MYTEXT)
) 
select lhs.*, sequence = count(*)
from data lhs
left join data rhs on rhs.FKID = lhs.FKID 
  and rhs.PKID <= lhs.PKID
group by 
  lhs.PKID,
  lhs.FKID,
  lhs.MYTEXT

产生这个:

PKID        FKID        MYTEXT sequence
----------- ----------- ------ -----------
1           1           one    1
2           1           two    2
3           1           three  3
5           1           five   4
6           1           six    5

【讨论】:

  • 抱歉,@Pieter,我无法了解如何将您的答案与我的插入语句和 XML 数据集成
  • (1) with 块只是提供一些测试数据进行验证。 (2) 剩余的select 语句旨在替换insert 语句中的select 子句。
猜你喜欢
  • 2020-11-04
  • 1970-01-01
  • 2013-04-12
  • 1970-01-01
  • 2016-11-22
  • 2013-05-26
  • 2014-09-09
  • 1970-01-01
  • 2019-03-25
相关资源
最近更新 更多