【问题标题】:Query only takes the first value of subquery查询只取子查询的第一个值
【发布时间】:2022-08-06 00:30:06
【问题描述】:

我正在创建一个 SQL 函数来报告,我收到的参数是几个字符串,其中包含由, 分隔的 PK 示例:

@ID_Model_list = \'1,2\'
@ID_Station_list = \'1,4,7,8,10\'

在我的 SQL 查询中,我执行了一个子查询,我将收到的变量转换为一列,例如:

SELECT CAST(VALUE AS INT) AS ID FROM string_split(@ID_Model_list,\',\')

将等同于:SELECT CAST(value AS int) AS ID FROM string_split(\'1,2\',\',\')

结果:

如果我将上面的代码添加到我的查询中,它只需要子查询产生的列的第一个值

CREATE FUNCTION V_Scrap_Report
 (@ID_Model_list varchar, @ID_Station_list varchar, @fecha datetime, @fechafin datetime)
 RETURNS TABLE
 AS RETURN
(SELECT   S.IDScrap
        , S.fecha
        , M.modelo
        , E.estacion 
        , C.correccion
        , S.elemento
        , P.nombre
        , P.numeroparte
        , Sp.cantidad
FROM            dbo.Scrap           S 
FULL OUTER JOIN dbo.Modelo          M   ON  S.IDModelo      =   M.IDModelo
FULL OUTER JOIN dbo.Estacion        E   ON  E.IDEstacion    =   S.IDEstacion
FULL OUTER JOIN dbo.Scrapcorreccion Sc  ON  S.IDScrap       =   Sc.IDScrap
FULL OUTER JOIN dbo.Correccion      C   ON  C.IDCorrecion   =   Sc.IDCorrecion 
FULL OUTER JOIN dbo.Scraparte       Sp  ON  S.IDScrap       =   Sp.IDScrap
JOIN        dbo.Parte           P   ON  Sp.IDParte      =   P.IDParte 
WHERE S.fecha   >= @fecha 
AND   S.fecha   <= DATEADD(HOUR,23.9999,@fechafin)
AND   S.IDModelo = (SELECT CAST(VALUE AS INT) AS ID FROM string_split(@ID_Model_list,\',\'))
AND   S.IDEstacion = (SELECT VALUE FROM string_split(@ID_Station_list,\',\')))

上面的函数只在S.IDModelo = 1ANDS.IDEstacion = 1没有考虑到有以下情况时才返回结果:

S.IDModelo = 2 AND S.IDEstacion = 1

S.IDModelo = 1 AND S.IDEstacion = 4

S.IDModelo = 1 AND S.IDEstacion = 7

S.IDModelo = 1 AND S.IDEstacion = 8

S.IDModelo = 2 AND S.IDEstacion = 10

当我调用该函数时,我会这样做:

SELECT * FROM V_Scrap_Report(\'1,2\',\'1,4,7,8,10\',\'2022-07-18\',\'2022-07-20\')

古怪的,如果我将... V_Scrap_Report(\'1,2\'... 更改为... V_Scrap_Report(\'2,1\'...,只需带上

S.IDModelo = 2 AND S.IDEstacion = 1

查询中可能缺少什么以免跳过匹配项?

  • 而不是=,您是否考虑过in
  • (@ID_Model_list varchar, 仔细看。你认为这个字符串参数的长度是多少?
  • DATEADD(HOUR,23.9999,@fechafin) 这并不像您认为的那样。文档对第二个参数的数据类型有什么说明?它被评估为一个 int。转换为 int 时 23.9999 是什么?
  • 斯图有同样的结果
  • 当您的WHERE 强制从表Scrap 中找到行并从Scraparte 中找到一行时,您为什么在这里使用FULL OUTER JOINs,因为必须找到Parte 中的一行并且Scraparte 是在其ON 中使用。

标签: sql sql-server


【解决方案1】:

cmets 和 Bohemian 的回答为您提供了一些您需要查看的查询有问题的具体问题,但我认为您真正需要的是不同的理解你在做什么。所以...

select 返回一个集合。 (技术上是一个包,因为它可以包含重复项,但我们将忽略这一点)。

一个集合可以有零个成员、一个成员或多个成员。有一组大于 1 小于 4 的整数,该组是{2, 3}。有一组小于 1 和大于 4 的整数,这个集合是 {} 又名“空集”。

因此,集合是零个或多个事物的集合,而不是一个事物。

当你在比较事物时,你不能将一组事物与一个事物进行比较。假设我左手拿着一袋橙子,右手拿着一个橙子。我右手的橙子和左手的橙子味道一样吗?这个问题真的没有意义,不是吗?你得拿我右手的那个橘子和每个袋子里的橙色,单独。

所以如果我让 SQL 评估是否orange_in_right_hand = { first_orange_in_bag, second_orange_in_bag, third_orange_in_bag } 你想要它做什么?这是一个没有真正意义的问题。

您的查询中有这样的情况:

where S.IDEstacion = (SELECT VALUE FROM string_split(@ID_Station_list,',')))

您与@ID_model_list 进行了类似的比较。

该操作的左侧是一个值。该操作的右侧是select 的结果,它可以返回多个值。在这种情况下,string_split 函数的输出。您正在要求 SQL 确定一件事是否是等于可能很多东西。

正如我们在橙子上看到的那样,这真的没有意义。而且因为没有意义,实际上会导致错误。

那么为什么你没有收到错误?因为在您的特定情况下,string_split 返回的集合将碰巧只有一个成员,因为您的代码中有错误。

让我们看看@ID_station_list。您对string_split 的输入是@ID_Station_list 参数,您说的是varchar。但是你没说多久.在这种情况下,这意味着它将被视为一个字符长:

declare @ID_station_list varchar;
set @ID_station_list = '1,4,7,8,10';
select @ID_station_list;

你认为这会返回什么?它将返回字符串值'1'。所有其他字符都被扔掉了,因为你没有说@ID_station_listvarchar 大到足以容纳你给它的价值。您刚刚说它是varchar,在这种情况下,SQL 会假定您的意思是varchar(1)

因此,您传递给string_split 函数的值就是值“1”。因此,当您拆分此字符串时,您会返回一个值。

SQL Server 然后会看着它并想“好吧,你问我单个值是否等于选择的结果,你真的不应该这样做,因为它没有意义,但在这种特殊情况我会在不告诉你问题的情况下为你做这件事,因为集合中只有一个成员”。

如果您通过将@ID_station_list 设为varchar(100) 并为其赋予值'1,2,3' 来修复您的参数声明,您将收到错误消息。

又怎样应该您将单个值IDEstacionstring_split 返回的集合进行比较?您告诉 SQL 检查该值是否为 in 集合而不是检查它是否等于集。因此斯图的评论。

【讨论】:

    【解决方案2】:

    如果将where 子句条件放在外部联接表上,则可以有效地将联接转换为内部联接。

    将此类条件移至连接条件:

    ...
    FROM            dbo.Scrap           S 
    FULL OUTER JOIN dbo.Modelo          M   ON  S.IDModelo      =   M.IDModelo
      AND S.IDModelo = (SELECT CAST(VALUE AS INT) AS ID FROM string_split(@ID_Model_list,','))
    FULL OUTER JOIN dbo.Estacion        E   ON  E.IDEstacion    =   S.IDEstacion
      AND S.IDEstacion = (SELECT VALUE FROM string_split(@ID_Station_list,',')))
    FULL OUTER JOIN dbo.Scrapcorreccion Sc  ON  S.IDScrap       =   Sc.IDScrap
    FULL OUTER JOIN dbo.Correccion      C   ON  C.IDCorrecion   =   Sc.IDCorrecion 
    FULL OUTER JOIN dbo.Scraparte       Sp  ON  S.IDScrap       =   Sp.IDScrap
    JOIN        dbo.Parte           P   ON  Sp.IDParte      =   P.IDParte 
    WHERE S.fecha   >= @fecha 
    AND   S.fecha   <= DATEADD(HOUR,23.9999,@fechafin)
    

    【讨论】:

    • 由于WHERE,这些JOINs 没有任何理由成为FULL OUTER JOINs。 Scraparte 加倍如此,因为 INNER JOINParte。除了Scarparte之外,也许大多数应该是LEFT JOINs。
    • 好奇,部分有效,如果 '...V_Scrap_Report('2,1' ...' 返回 'S.IDModel' = 1 作为 'null' 并且如果 '...V_Scrap_Report('1,2' ...'返回 '​​S.IDModel' = 2 as 'null' 与 S.IDStation 发生同样的事情都是 'null'
    • @Larnu,谢谢,我已经修改了“JOINS”
    • @Robinson 在没有连接时返回 null 是外连接的唯一目的。
    猜你喜欢
    • 2021-09-07
    • 2022-10-19
    • 1970-01-01
    • 2018-12-01
    • 2022-11-30
    • 1970-01-01
    • 2020-03-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多