如何使用行组中的值根据另一个表更新一个表?
我有两个表:
CREATE TABLE Employee (
Site ???? ????,
WorkTypeId char(2) NOT NULL,
Emp_NO int NOT NULL,
"Date" ???? NOT NULL
);
CREATE TABLE PTO (
Site ???? ????,
WorkTypeId char(2) NULL,
Emp_NO int NOT NULL,
"Date" ???? NOT NULL
);
我想更新 PTO
的 WorkTypeId
列中的值:
Employee
中的EMP NO
(查找表)和PTO
应匹配。- 应仅从该月的第一次出现中选取单个
WorkTypeId
值。
例如,给定以下示例输入数据:
TABLE Employee
:
Site | WorkTypeId | Emp_NO | Date |
---|---|---|---|
5015 | MB | 1005 | 2022-02-01 |
5015 | MI | 1005 | 2022-02-04 |
5015 | PO | 1005 | 2022 -02-04 |
5015 | ME | 2003 | 2022-01-01 |
5015 | TT | 2003 | 2022-01-10 |
表 PTO
:
站点 | WorkTypeId | Emp_NO | 日期 |
---|---|---|---|
5015 | 1005 | 2022-02-03 | |
5015 | 1005 | 2022-02-14 | |
5014 | 2003 | 2022-01-09 |
例如:
- 给定
Employee
和Emp_NO = 1005
...- ...
Employee
表中该Emp_NO
有 3 行,具有 3 个不同的WorkTypeId
值,但日期
值。 - 因此,选择最早的
Date
(2022-02-01) 的WorkTypeId
值,即'MB'
- 因此,
Emp_NO
得到WorkTypeId = 'MB'
。 - 并使用该单个值填充
PTO
表中1005
的WorkTypeId
单元格。 - 还可以按月进行匹配。
- ...
因此,PTO
表中的预期输出为
Site | WorkTypeId | Emp_NO | Date |
---|---|---|---|
5015 | MB | 1005 | 2022-02-03 |
5015 | MB | 1005 | 2022-02-14 |
5014 | ME | 2003 | 2022-01-09 |
I have two tables:
CREATE TABLE Employee (
Site ???? ????,
WorkTypeId char(2) NOT NULL,
Emp_NO int NOT NULL,
"Date" ???? NOT NULL
);
CREATE TABLE PTO (
Site ???? ????,
WorkTypeId char(2) NULL,
Emp_NO int NOT NULL,
"Date" ???? NOT NULL
);
I would like to update values in PTO
's WorkTypeId
column:
EMP NO
inEmployee
(the lookup table) andPTO
should match.- A single
WorkTypeId
value should be picked from only the first occurrence of the month.
For example, given this sample input data:
TABLE Employee
:
Site | WorkTypeId | Emp_NO | Date |
---|---|---|---|
5015 | MB | 1005 | 2022-02-01 |
5015 | MI | 1005 | 2022-02-04 |
5015 | PO | 1005 | 2022-02-04 |
5015 | ME | 2003 | 2022-01-01 |
5015 | TT | 2003 | 2022-01-10 |
TABLE PTO
:
Site | WorkTypeId | Emp_NO | Date |
---|---|---|---|
5015 | 1005 | 2022-02-03 | |
5015 | 1005 | 2022-02-14 | |
5014 | 2003 | 2022-01-09 |
For example:
- Given
Employee
withEmp_NO = 1005
...- ...there are 3 rows for that
Emp_NO
in theEmployee
table, with 3 distinctWorkTypeId
values, but differingDate
values. - So pick the
WorkTypeId
value for the earliestDate
(2022-02-01), which is'MB'
- So
Emp_NO
getsWorkTypeId = 'MB'
. - And use that single value to fill
1005
'sWorkTypeId
cells in thePTO
table. - But also match by month.
- ...there are 3 rows for that
So the expected output in the PTO
table is
Site | WorkTypeId | Emp_NO | Date |
---|---|---|---|
5015 | MB | 1005 | 2022-02-03 |
5015 | MB | 1005 | 2022-02-14 |
5014 | ME | 2003 | 2022-01-09 |
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
更新 2002-03-05
将其留在这里供后代使用,但我建议阅读 Dai 的精彩文章解决这个问题的不同方法。
尝试交叉应用来获取第一个具有匹配月份和年份的员工记录。
注意:使用 OUTER APPLY 始终返回所有 PTO 记录,即使没有找到匹配的 WorkTypeId。
结果:
db<>fiddle 此处
Update 2002-03-05
Leaving this here for posterity, but I'd recommend reading Dai's excellent write up on different approaches to this problem.
Try a CROSS APPLY to grab the first Employee record with a matching month and year.
Note: Use OUTER APPLY to always return all PTO records, even when no matching WorkTypeId was found.
Results:
db<>fiddle here
从与
GROUP BY
查询中的MIN
/MAX
表达式中使用的列不同获取值在 SQL 中仍然是一件令人惊讶的困难事情,虽然 SQL 语言(和 SQL Server)的现代版本使它变得更容易,但它们对大多数人来说完全不明显且违反直觉,因为它必然涉及更高级的主题,例如CTE,派生表(又名内部查询)、自连接和窗口函数,尽管查询在概念上很简单。无论如何,与现代 SQL 一样,通常有 3 或 4 种不同的方法来完成相同的任务,但也有一些陷阱。
前言:
由于
Site
、Date
、Year
和Month
都是T-SQL中的关键字,我用双引号对它们进行了转义,这是符合 ISO/ANSI SQL 标准的转义保留字的方法。SET QUOTED IDENTIFIER OFF
,则将双引号更改为方括号:[]
我假设
两个表中的 Site
列只是一个普通的“ol”数据列,如下所示:PRIMARY KEY
成员列。GROUP BY
。JOIN
谓词中使用它。以下所有方法均假定此数据库状态:
Employee
和PTO
的 CTEe
和p
分别添加计算的"Year"
和"Month"
列,从而避免重复使用YEAR( "Date" ) AS "Year"
在GROUP BY
中并且JOIN
表达式。方法 1:使用基本聚合组合 CTE,然后更新:
这是 SSMS 的屏幕截图,显示了上述查询运行之前和之后
PTO
表的内容:< a href="https://i.sstatic.net/5eQxN.png" rel="nofollow noreferrer">
方法 2:使用
FIRST_VALUE
跳过自JOIN
:此方法提供了更短、稍微简单的查询,但需要SQL Server 2012 或更高版本(并且您的数据库在兼容级别 110 或更高版本中运行)。
令人惊讶的是,您不能在以下位置使用
FIRST_VALUE
GROUP BY
查询,尽管它与MIN
明显相似,但可以使用SELECT DISTINCT
构建等效查询:运行后
SELECT * FROM PTO
给出了与方法 2 完全相同的输出。方法 2b,但更短:
这样 @SOS 就不会觉得自己太自鸣得意了。 SQL 比我的要短得多
Getting a value from a column different to the column used in a
MIN
/MAX
expression in aGROUP BY
query still remains a surprisingly difficult thing to do in SQL, and while modern versions of the SQL language (and SQL Server) make it easier, they're completely non-obvious and counter-intuitive to most people as it necessarily involves more advanced topics like CTEs, derived-tables (aka inner-queries), self-joins and windowing-functions despite the conceptually simple nature of the query.Anyway, as-ever in modern SQL, there's usually 3 or 4 different ways to accomplish the same task, with a few gotchas.
Preface:
As
Site
,Date
,Year
, andMonth
are all keywords in T-SQL, I've escaped them with double-quotes, which is the ISO/ANSI SQL Standards compliant way to escape reserved words.SET QUOTED IDENTIFIER OFF
then change the double-quotes to square-brackets:[]
I assume that the
Site
column in both tables is just a plain' ol' data column, as such:PRIMARY KEY
member column.GROUP BY
.JOIN
predicate.All of the approaches below assume this database state:
e
andp
that extendEmployee
andPTO
respectively to add computed"Year"
and"Month"
columns, which avoids having to repeatedly useYEAR( "Date" ) AS "Year"
inGROUP BY
andJOIN
expressions.Approach 1: Composed CTEs with elementary aggregates, then
UPDATE
:Here's a screenshot of SSMS showing the contents of the
PTO
table from before, and after, the above query runs:Approach 2: Skip the self-
JOIN
withFIRST_VALUE
:This approach gives a shorter, slightly simpler query, but requires SQL Server 2012 or later (and that your database is running in compatibility-level 110 or higher).
Surprisingly, you cannot use
FIRST_VALUE
in aGROUP BY
query, despite its obvious similarities withMIN
, but an equivalent query can be built withSELECT DISTINCT
:Doing a
SELECT * FROM PTO
after this runs gives me the exact same output as Approach 2.Approach 2b, but made shorter:
Just so @SOS doesn't feel too smug about their SQL being considerably more shorter than mine ????, the Approach 2 SQL above can be compacted down to this:
Approach 1's plan looks like this:
Approach 2b's plan looks like this:
@SOS's plan, for comparison, is a lot simpler... and I honestly don't know why, but it does show how good SQL Server's query optimizer is thesedays: