【问题标题】:MSSQL - Need to query multiple records into one record / set of results - based on Id and DateMSSQL - 需要将多条记录查询到一条记录/一组结果中 - 基于 Id 和 Date
【发布时间】:2020-08-07 21:38:58
【问题描述】:

**更新标题..

首先:我知道这听起来是一个重复的问题,但我从昨天下午开始就在网上搜索解决方案。

我有 2 张桌子,这是我的测试资料:

create table #UserMaster
(
    UserId int identity(1,1),
    FirstName varchar(50),
    LastName varchar(50),
    Department smallint,
    JobCode varchar(8),
);

create table #UserUpdateInfoMain
(
    Id int identity(1,1),
    UserId int,
    DateRequested smalldatetime,
    SubmittedBy varchar(20),
    RequestedBy varchar(20),
    NameChange varchar(50),
    TransferDept smallint,
    TransferJob varchar(8),
    TerminationDate date
);

我们的人力资源部门将提交更改员工姓名的请求、调动请求(针对工作或部门)以及终止请求。此表 (UserUpdatedInfoMain) 背后的想法是容纳所有此类请求,并将它们与主要用户信息(在 UserMaster 中)适当地链接起来。因此,当我们在数据库中搜索用户时,我们的想法是能够看到他们当前的姓氏、当前的部门/工作分配,或者他们是否已经被终止 - 也能够看到所有的线索这样的改变是一个加分项。

将收到一个更改 Bob 姓名的请求。一周后 Bob 可能会更换职位,因此会收到部门调动请求。我正在尝试查询:Bob -> NewName -> NewDept(一条记录,而不是多条记录)

它的诀窍是可能有多个名称更改和转移,因此我需要获取每个名称中的最新名称以显示在查询中。我发现了一些有点相似的东西,我似乎无法开始工作(包括使用 min/max - 如果它只是在寻找一个结果,效果很好),我设法得到了一次结果另一行出现在某一时刻,只是无法丢弃所有不必要的结果。

这是我正在使用的示例数据:

插入#UserMaster(名字,姓氏,部门,工作代码) 值('约翰','史密斯',1212,'A1234'),--1 ('詹姆斯','托德',1232,'B2345'),--2 ('斯蒂芬','威廉姆斯',1212,'A1234'),--3 ('凯西','贝茨',1212,'C2342'),--4 ('Rob','Johnson',3434,'A1234'),--5 ('Cindy','Lou',2314,'A1234'), ('杰西','日期',2323,'D3422'), ('苹果','苹果酒',1342,'B2312'), ('百事可乐','可乐',1432,'A1234'), ('随机','鲍勃',1342,'C3421'), ('绿巨人','霍根',3422,'C3221'), ('约翰','Cena',3432,'B2231'), ('萨沙','班克斯',2321,'B2312'), ('Jimmy','Iovine',3432,'A1234'), ('Dwayne','约翰逊',1325,'C2342'), ('悉尼','皮尔斯',1241,'A1234'), ('Expo','Marks',4321,'B2312'), ('Pat','Swizzle',2521,'C2342'), ('猴子','骨头',1212,'D3422'), ('快乐','吉尔摩',4545,'D3422'); 插入#UserUpdateInfoMain(UserId、DateRequested、SubmittedBy、RequestedBy、NameChange、TransferDept、TransferJob、TerminationDate) 值 (1, cast(getdate()-5 as smalldatetime), 'rob', 'bob', 'Waters1', NULL, NULL, NULL), (2, cast(getdate()-4 as smalldatetime), 'rob', 'bob', NULL, 7878, 'J7098', NULL), (3, cast(getdate()-3 as smalldatetime), 'rob', 'bob', 'Cider1', NULL, NULL, NULL), (4, cast(getdate()-4 as smalldatetime), 'rob', 'bob', NULL, NULL, NULL, cast(getdate()-3 as date)), (5, cast(getdate()-2 as smalldatetime), 'rob', 'bob', NULL, 9898, NULL, NULL), (1, cast(getdate()-3 as smalldatetime), 'jim', 'bob', 'Lakely2', NULL, NULL, NULL), (1, cast(getdate() as smalldatetime), 'sue', 'bob', 'Salsa3', NULL, NULL, NULL), (1, cast(getdate() as smalldatetime), 'sue', 'bob', NULL, 9648, 'K9487', NULL), (2, cast(getdate()-1 as smalldatetime),'rosco', 'bob', 'Mordor1', NULL, NULL, NULL), (2, cast(getdate() as smalldatetime), 'rosco', 'bob', 'Elves2', NULL, NULL, NULL);

最终应该出现的两个示例如下所示...当然,其他 3 个也应该出现

(#UserUpdateInfoMain)

UserId    NameChange   TransferDept    TransferJob   TerminationDate
  1         Salsa3       9648           K9487         NULL 
  2         Elves2       9343           H8898         NULL

为这篇冗长的帖子道歉,我只是想确保我解释得体面,并有一个我正在玩的东西的工作示例。我也很抱歉没有为我查看和完成的所有内容提供链接/示例。

我尝试在每列上为最大日期进行 CTE,并将其与 #UserUpdatedInfoMain 表连接起来,但这并没有完全奏效;我无法找到正确的组合来让它们正常工作。

示例:

theNameChanged_cte (UserId, Req_Date)
as
(
select
    i2.UserId,
    max(i2.DateRequested) as Req_Date
from #UserUpdateInfoMain i2
group by i2.UserId
)

...or...

theNameChanged_cte (UserId, Req_Date, NameChange)
as
(
select
    i2.UserId,
    max(i2.DateRequested) as Req_Date,
    i2.NameChange
from #UserUpdateInfoMain i2
group by i2.UserId, i2.NameChange
having i2.NameChange is not null
)

第一个合法的 CTE 只是查找给定 UserId 的最近日期 - 好的,很酷......这已经完成了。第二个请求所有的 NameChange 请求,所以一些 UserId 出现了两次。如果我可以将 MAX 与 where 语句一起使用,我想我可以得到它。不幸的是,我认为我的大脑在这一点上只是被炸了。

任何和所有的帮助、建议和建议 - 甚至“查看这种类型的功能”都将不胜感激(实际的解决方案会更好!但我不会接受建议并与他们合作)。我希望我在这篇文章中获得了所有必要的信息以获得帮助。

【问题讨论】:

    标签: sql-server tsql


    【解决方案1】:

    这里有一个 SQL hack:

    DECLARE
        @UserId INT, @NameChange VARCHAR(50), @TransferDept smallint, @TransferJob VARCHAR(8), @TerminationDate DATE
    
    SET @UserId = 1;
    SELECT
        @NameChange = CASE WHEN @NameChange IS NULL THEN NameChange ELSE @NameChange END,
        @TransferDept = CASE WHEN @TransferDept IS NULL THEN TransferDept ELSE @TransferDept END, 
        @TransferJob = CASE WHEN @TransferJob IS NULL THEN TransferJob ELSE @TransferJob END,
        @TerminationDate = CASE WHEN @TerminationDate IS NULL THEN TerminationDate ELSE @TerminationDate END
    FROM #UserUpdateInfoMain ui
    WHERE
        ui.UserId = @UserId
    ORDER BY
        ui.Id DESC;
    
    SELECT
        @UserId AS UserId,
        @NameChange AS NameChange,
        @TransferDept AS TransferDept,
        @TransferJob AS TransferJob,
        @TerminationDate AS TerminationDate;
    

    返回

    +--------+------------+--------------+-------------+-----------------+
    | UserId | NameChange | TransferDept | TransferJob | TerminationDate |
    +--------+------------+--------------+-------------+-----------------+
    |      1 | Salsa3     |         9648 | K9487       | NULL            |
    +--------+------------+--------------+-------------+-----------------+
    

    【讨论】:

    • 太完美了!!太感谢了!接下来我要做的是盯着它并分析它,直到我理解它(我同意)哈哈哈......我的意思是,我明白了要点,但它看起来如此倒退和曲折 - 我喜欢它。再次感谢您的快速解决,并向我展示了一些新的使用和学习的东西 - 您不知道我是多么感激这一点。
    • 好的,我了解关键部分 - 按表的 ID 排序,值越高越近。接下来是“当变量为空时,离开并继续,但如果不是,则为变量赋值”。第二个 Select 语句是打印值的语句。我可以对此进行研究,但是如果您有答案,我会全力以赴。第一个选择语句。我正在努力它如何循环记录 - 我的意思是,变量似乎得到赋值的唯一时间是当变量为空时,否则我什至看不到被调用的字段......是因为变量名?
    • 这是因为字段名称在 case/when 逻辑赋值中 - 这是我对这两个帖子的理解:1) Microsoft Doc 2) StackOverflow - 此链接上的最后一个解决方案。
    • 这种技术是 SQL Server 的一个未记录的“功能”,因为您可以在整个数据行集上分配一个变量(例如,@NameChange)。通过对 Id 列进行降序排序,您可以确保首先显示最新的值,并将其与 NULL 的检查相结合,一旦变量捕获了最新的值,它将不再设置新值。这是一个长期以来一直受到弃用威胁的功能,但它仍然存在,并且在过去为我省去了一些麻烦,就像你在这里一样。
    猜你喜欢
    • 1970-01-01
    • 2015-04-12
    • 1970-01-01
    • 1970-01-01
    • 2017-10-31
    • 2017-05-08
    • 1970-01-01
    • 2017-07-18
    • 2017-02-24
    相关资源
    最近更新 更多