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 中,如果您使用= 将一件事与另一件事进行比较,则左侧和右侧都应该是标量类型。 “单个值”,例如 intfloatboolean。不设置。

从根本上说,这与您不能这样做的原因相同:

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 返回太多值

Couchbase cli cluster-init 返回“太多值无法解包”

CSV 中返回的 Solr 多值字段

OC中传入参数,返回多值

s-s-rS 多值参数只返回最后一个值