【问题标题】:SQL, foreach-loop-like INSERT based on inserted value that need to be re-selected, possible?SQL,foreach-loop-like INSERT 基于需要重新选择的插入值,可能吗?
【发布时间】:2019-07-31 09:45:24
【问题描述】:

现状

我有 4 个表格,看起来像这样 满足以下条件

  • DataMessages.SenderID 是一个 FK,以 DataUser.UserID 为参考
  • Data_UserGroup.UserID 是一个 FK,以 DataUser.UserID 为参考
  • DataMessageRecipient.RecipID 是一个 FK,以 DataUser.UserID 为参考
  • DataMessageRecipient.DestID 是一个 FK,以 DataUser.DestID 为参考
  • DataMessageRecipient.MssgID 是一个 FK,以 DataMessages.ID 为参考
  • DataMessages.IDDataMessageRecipient.EntryIDIDENTITY()

还有这个AFTER INSERT dbo.DataMessages 表上的触发器

CREATE TRIGGER OnNewQueue ON dbo.DataMessages AFTER INSERT AS 
BEGIN
    SET NOCOUNT ON;
    DECLARE @inTabs Table
    (ID INT);
    /*Part that fetch the IDENTITY() column*/
    INSERT INTO @inTabs SELECT DataMessages.ID FROM DataMessages
        INNER JOIN inserted INS ON
            INS.SenderID = DataMessages.SenderID AND
            INS.MssgID = DataMessages.MssgID AND
            INS.CreatedAt = DataMessages.CreatedAt;
    /*
        DataMessages.CreatedAt (DateTime2), DataMessages.MssgID (int) are
        ommited from the picture, but those columns AND-ed will guaranteed to
        return unique rows because a SenderID cannot have duplicate MssgID
        even more on same CreatedAt time
    */
    INSERT INTO dbo.DataMessageRecipient (MssgID, RecipID, DestID)
        SELECT 
            inTbl.ID,
            DataUser.UserID,
            DataUser.DestID 
        FROM
            @inTabs AS inTbl,
            DataUser
        WHERE
            DataUser.UserID IN
            (
                SELECT Data_UserGroup.UserID 
                FROM Data_UserGroup
                WHERE Data_UserGroup.GroupID IN
                (
                    SELECT Data_UserGroup.GroupID 
                    FROM Data_UserGroup, inserted inr
                    WHERE Data_UserGroup.UserID = inr.SenderID
                )
            );
END

我想要实现的目标

我想要实现的是,foreach 插入DataMessages,无论是从单行插入还是多行插入,插入DataMessageRecipient(s) 的条件如下:

  1. RecipIDDestID 来自 DataUser 表,其中 DataMessages.SenderID 共享相同的 Data_UserGroup.GroupID(s)
  2. MssgID 来源于插入的DataMessages.IDRecipID 与插入的 SenderID 相关,基于 No.1 条件
  3. (SQL 的可选)插入的收件人不能包含其自己的 SenderID 作为 RecipID

问题

INSERT INTO dbo.DataMessages (SenderID) 
    (10)

INSERT INTO dbo.DataMessages (SenderID) 
    (-83)

INSERT INTO dbo.DataMessages (SenderID) 
    (2213)

上述命令/查询的行为和结果符合预期,因为它们中的每一个都是不同的单独查询(满足要求,异常条件编号 3),当多行 INSERT 尝试时(例如,像这样),就会出现问题

INSERT INTO dbo.DataMessages (SenderID) 
    (10), (83), (2213)

预期的结果是,无论是单行插入还是多行插入,foreach 插入DataMessages,每个插入的都会有“自己的”DataMessageRecipient 条目,RecipID 与每个插入的SenderID 相关,基于@987654356 中的条目@ 其中RecipIDSenderID 共享相同的GroupID(s)

目前在多行 INSERT 上的结果是,对于每个插入的 DataMessages,“收件人”被合并,因此所有新插入的 DataMessage 将共享相同的收件人

示例

考虑以下数据:

|     DataUser    |   |    Data_UserGroup  |   |   DataMessages  |
|:------:|:------:|:-:|:------:|:---------:|:-:|:----:|:--------:|
| UserID | DestID |   | UserID |  GroupID  |   |  ID  | SenderID |
|    1   |   -1   |   |    4   |     10    |   |  100 |     4    |
|    2   |   -2   |   |    5   |     10    |   |  101 |     1    |
|    3   |   -3   |   |    2   |     11    |   |  102 |     5    |
|    4   |   -4   |   |    1   |     12    |   |      |          |
|    5   |   -5   |   |    1   |     13    |   |      |          |
|    6   |   -6   |   |    3   |     13    |   |      |          |
|        |        |   |    5   |     13    |   |      |          |
|        |        |   |    6   |     13    |   |      |          |

不管INSERT 的“方式”,它应该是由这个DataMessageRecipient 产生的

| EntryID | MssgID | RecipID | DestID |
|:-------:|:------:|:-------:|:------:|
|         |   100  |    4    |   -4   |
|         |   100  |    5    |   -5   |
|         |   101  |    1    |   -1   |
|         |   101  |    3    |   -3   |
|         |   101  |    5    |   -5   |
|         |   102  |    5    |   -5   |
|         |   102  |    4    |   -4   |
|         |   102  |    1    |   -1   |
|         |   102  |    3    |   -3   |

/*
 Note The RecipID is STILL included as the recipients.
 This is 'okay' for now, as the logic 
 outside SQL can filter this out
*/

但是,如果 DataMessage 插入多行 INSERT,则会产生如下结果:

| EntryID | MssgID | RecipID | DestID |
|:-------:|:------:|:-------:|:------:|
|         |   101  |    1    |   -1   |
|         |   101  |    3    |   -3   |
|         |   101  |    4    |   -4   |
|         |   101  |    5    |   -5   |
|         |   102  |    1    |   -1   |
|         |   102  |    3    |   -3   |
|         |   102  |    4    |   -4   |
|         |   102  |    5    |   -5   |
|         |   103  |    1    |   -1   |
|         |   103  |    3    |   -3   |
|         |   103  |    4    |   -4   |
|         |   103  |    5    |   -5   |

问题

我怎样才能达到预期的效果?这有可能在 SQL 逻辑中做这样的事情吗?或者我应该拉出插入的 DataMessages 结果并在后端逻辑中对其进行后处理,我可以在其中为每个循环执行 foreach 插入的 ID?

谢谢

【问题讨论】:

  • FROM @inTabs AS inTbl, DataUser 你是故意在这里使用旧样式CROSS JOIN 吗?
  • @Larnu,我认为不是故意的,因为在正常的JOIN 没有ON 的情况下,我的 SQL Studio 不断抛出红色下划线错误并从 Stackoverflows Q&A 中读取一个“简单”的方式来拍打两个 SELECT与CROSS JOIN 没有任何关联的结果
  • @Larnu,它适用于单行插入的情况,所以 . . .我不知道这是故意的还是我只是不知道这是否是“旧式”
  • 要获取范围内最后插入的标识值,请尝试 scope_identity()。您可以设置一个 var declare @scope_identity int = scope_identity() 并使用该 var 插入到您的其他表中。
  • @JBJ。是的,我知道我可以使用 scope_identity(),但是如果通过单个命令/查询(据我所知)出现多行 INSERT,则 scope_identity() 只会为您提供最后插入的行的标识。这就是为什么我选择手动SELECT“插入的”ID

标签: sql sql-server tsql triggers relationship


【解决方案1】:

触发器可以访问插入的,拉出任何插入的 id

create trigger theTriggerName 
    on dbo.DataMessages for update
as
insert into dbo.DataMessagesInserted (insertedID)
select ID from inserted
GO

【讨论】:

    【解决方案2】:

    你得到这种行为的原因是你最里面的查询:

    SELECT Data_UserGroup.GroupID 
    FROM Data_UserGroup, inserted inr
    WHERE Data_UserGroup.UserID = inr.SenderID
    

    返回插入时适用的所有 GroupID。

    因此,当您执行单个插入时,它可以工作,但是当您插入多个时,您将获得所有行的组,并且对于每个 DataMessage,您将获得与 Data_UserGroup 中包含对应于任何 GroupID 的行一样多的行您的查询。

    另外:为什么要使用噱头来获取身份列? INSERTED 表也有这一列。

    这是我的建议:

    (根本不需要插入@inTabs)

    insert into dbo.DataMessageRecipient (MssgID, RecipID, DestID)
        select
            ins.ID as MssgID
            ,g2.UserID as RecipID
            ,u.DestID as DestID
        from
            inserted ins
            inner join Data_UserGroup g on ins.SenderID=g.UserID -- first join to get the groupID
            inner join Data_UserGroup g2 on g2.GroupID=g1.GroupID -- again join to get all userids of this groupid
            inner join DataUser u on g2.UserID=u.UserID -- to get destID
    

    另外,如果你想省略原来的userId,你可以在最后加上:

    where ins.SenderID<>g2.UserID

    【讨论】:

      猜你喜欢
      • 2020-05-04
      • 2016-02-25
      • 1970-01-01
      • 2018-08-24
      • 1970-01-01
      • 2013-08-02
      • 2011-08-20
      • 2011-04-24
      • 1970-01-01
      相关资源
      最近更新 更多