IF 条件返回太多值
Posted
技术标签:
【中文标题】IF 条件返回太多值【英文标题】:IF Condition Returning too Many Values 【发布时间】:2021-09-15 20:46:41 【问题描述】:我对 t-sql 世界很陌生,我正在尝试创建一个查询,该查询将根据多个条件更改值。
TSH1 是将更改值的主表。 Freightview 是包含我需要添加到 TSH1 中的运费金额的表。
我希望查询查找表之间的匹配项,如果存在匹配项,请更改 FREIGHT 行(如果存在)。如果 FREIGHT 行不存在,则需要在 Freightview 表中添加包含发票金额的行。
我的问题是 IF 语句。它返回两个许多值以使查询正常工作。我需要改变什么?
最后两个查询是返回不在每个表中的值。
SELECT *
FROM TSH1 T
JOIN Freightview FR on FR.[Shippers number] = T.sonum
IF
((SELECT [Shippers number] FROM Freightview) = (SELECT sonum FROM TSH1 T WHERE EXISTS(SELECT * FROM TSH1 T WHERE T.productnum = 'FRT-OUT' OR T.productnum = 'FRT-IN' OR T.productnum = 'FRT')))
BEGIN
UPDATE TSH1 SET tcost = FR.[Invoice Amount] FROM TSH1 T INNER JOIN Freightview FR on FR.[Shippers number] = T.sonum
WHERE T.productnum = 'FRT-OUT' OR T.productnum = 'FRT-IN' OR T.productnum = 'FRT';
END
ELSE IF
((SELECT [Shippers number] FROM Freightview) = (SELECT sonum FROM TSH1 T WHERE NOT EXISTS(SELECT * FROM TSH1 T WHERE T.productnum = 'FRT-OUT' OR T.productnum = 'FRT-IN' OR T.productnum = 'FRT')))
BEGIN
SELECT * INTO temp_table FROM TSH1 T INNER JOIN Freightview FR on FR.[Shippers number] = T.sonum
WHERE FR.[Shippers number] = T.sonum AND NOT EXISTS (SELECT productnum from TSH1 T where T.productnum = 'FRT-OUT' OR T.productnum = 'FRT-IN' OR T.productnum = 'FRT');
UPDATE temp_table SET temp_table.productnum = 'FRT', [Invoice Amount] = TT.tcost, temp_table.productid = '7240', temp_table.pd = 'FREIGHT', temp_table.qtyfulfilled = 1,
temp_table.tprice = 0, temp_table.stdcost = 0, temp_table.flag = 'D', temp_table.avgcost = NULL
FROM temp_table TT
INNER JOIN Freightview FR on TT.sonum = FR.[Shippers number];
UPDATE temp_table SET ID=NULL;
DELETE x FROM (
SELECT *, rn=row_number() over (partition by TT.sonum order by TT.soid)
FROM temp_table TT
) x
WHERE rn > 1;
INSERT INTO TSH1 SELECT * FROM temp_table;
DROP TABLE temp_table;
END
ELSE
BEGIN
SELECT *
FROM TSH1 T
LEFT JOIN Freightview FR on T.sonum = FR.[Shippers number]
WHERE FR.[Shippers number] IS NULL;
END
BEGIN
SELECT *
FROM Freightview FR
LEFT JOIN TSH1_Backup T on T.sonum = FR.[Shippers number]
WHERE T.sonum IS NULL;
END
END```
【问题讨论】:
我强烈建议不要使用 MERGE(一些原因和进一步阅读的链接in this answer。请参阅我建议的 UPSERT 模式in this post。 【参考方案1】:使用 SQL,您通常必须“分组思考”。例如,select
语句返回一组值,而不仅仅是单个值1。
如果我select * from T
,结果可能会有多行。
如果我insert T1 select * from T2
,可能会在T1
中插入多行。
所以,像这样的声明
if ((select c from T1) = (select c from T2))
有点奇怪。我们在这里比较的是什么?在左侧,我们有零或多行来自 T1,而在右侧,我们有零或多行来自 T2。
现在,你可能在想自己......
答案很明显。如果两个结果集相同,那么相等比较应该返回true吧?
嗯……是的。如果我们能做到这一点,那就太好了。但这需要 SQL 将 select 语句的结果视为“具有成员值相等语义的匿名集合类型”。 SQL 并没有像一门语言那样复杂。在 SQL 中,如果您使用=
将一件事与另一件事进行比较,则左侧和右侧都应该是标量类型。 “单个值”,例如 int
、float
或 boolean
。不设置。
从根本上说,这与您不能这样做的原因相同:
create table T1(i int);
create table T2(j int);
if (T1 = T2) print 'tables had exactly the same content`;
那么,如何获得“告诉我 T1 和 T2 的内容是否完全匹配?”的语义。没有紧凑的语法可以做到这一点,你必须详细说明它,有很多不同的方式可以“表达”这个问题,而且很容易出错。这是一种正确的方法:
create table T1(i int);
create table T2(j int);
if not exists
(
select *
from T1
full join T2 on T1.i = T2.j
where T1.i is null or T2.j is null
) print 'tables had exactly the same content';
逻辑是“尽可能匹配每一行,并告诉我是否有任何无法匹配的行”。
现在,有趣的是,SQL 在实际得到结果之前不会“验证”比较,所以如果您的每个选择语句恰好返回单行单列,那么结果select 语句的值被视为标量值,而不是集合,然后相等比较起作用。我有点希望它没有,因为它不一致并且使人们感到困惑:
create table T1(i int);
create table T2(j int);
insert T1 values (1);
insert T2 values (1);
-- This will unfortunately succeed, and do what you intuitively "expect".
if ((select i from T1) = (select j from T2))
print 'tables both exactly one row with the same value';
但是如果我将更多行放入其中一个表中呢?
create table T1(i int);
create table T2(j int);
insert T1 values (1), (2);
insert T2 values (1);
-- This will fail
if ((select i from T1) = (select j from T2))
print 'tables both exactly one row with the same value';
错误是:
子查询返回超过 1 个值。当子查询跟随 =、!=、、>= 或子查询用作表达式时,这是不允许的。
你有一些 SQL 犯了同样的错误:
if ((select [shippers number] from Freightview) = -- ...
我希望这能回答您关于为什么会收到错误的具体问题。但是等一下,让我们回过头来看看你的要求:
我希望查询查找表之间的匹配项,如果存在匹配项,请更改 FREIGHT 行(如果存在)。如果 FREIGHT 行不存在,则需要在 Freightview 表中添加包含发票金额的行。
因此,您需要插入和更新的组合,具体取决于数据。一个"upsert"。
TSQL 有一个语句可以做到这一点:Merge。这是一个简化的示例来演示如何使用它。
create table T1(i int, c char);
create table T2(j int, c char);
insert T1 values (1, 'a');
insert T2 values (1, 'b'), (2, 'c');
merge T1 -- T1 will be "target" in the rest of the merge statement
using T2 on t2.j = T1.i -- T2 will be "source" in the rest of the merge statment
when matched then
update
set T1.c = T2.c
-- "target" isn't an alias defined by me. It's defined by the structure of "merge"
-- So this condition translates to "if there is a row in T2 with no matching row in T1"
when not matched by target then
insert (i, c)
values (T2.j, T2.c);
select * from T1;
/* result:
i c
----
1 b
2 c
*/
格式化merge
语句很难,我从来没有找到让我完全满意的方法。
1 这不太准确。 SQL 允许在表、结果集等中存在重复的行。在数学集中不能有重复的成员。所以从技术上讲,你必须“在袋子里思考”。但尽管如此,人们还是倾向于说“在集合中思考”。
【讨论】:
非常感谢,这是一个巨大的帮助!我会尝试合并语句。以上是关于IF 条件返回太多值的主要内容,如果未能解决你的问题,请参考以下文章
MS Access 2010 - 使用 RIGHT JOIN 的 SQL 查询 - 返回太多值
使用 Redshift 数据库时,SQL Join 或 SUM 返回太多值