【问题标题】:SQL - TOP 1 and MIN doesn't work but MAX worksSQL - TOP 1 和 MIN 不起作用,但 MAX 起作用
【发布时间】:2014-05-26 14:37:21
【问题描述】:

我这里有一个奇怪的错误。

我正在尝试将以下查询的结果限制为仅一行/结果:

SELECT userEmail
    FROM [LunchUsersAvailable]
    WHERE LunchGroupsID = '301' AND availableDate = '2015-01-01' AND userEmail != 'userEmail@company.com' EXCEPT
    (
        SELECT DISTINCT (COALESCE([user1], [user2])) AS matchedEmail
        FROM
        (
                SELECT [user1], [user2]
                FROM [LunchMatched]
            UNION ALL
                SELECT [user2], [user1]
                FROM [LunchMatched]
        ) AS tmp
        WHERE (user1 = 'userEmail@company.com' OR user2 = 'userEmail@company.com')
    )

输出:

test2@company.com

test3@company.com

test4@company.com


我尝试使用 TOP 1。没用。完全没有结果。

SELECT TOP 1 userEmail
    FROM [LunchUsersAvailable]
    WHERE LunchGroupsID = '301' AND availableDate = '2015-01-01' AND userEmail != 'userEmail@company.com' EXCEPT
    (
        SELECT DISTINCT (COALESCE([user1], [user2])) AS matchedEmail
        FROM
        (
                SELECT [user1], [user2]
                FROM [LunchMatched]
            UNION ALL
                SELECT [user2], [user1]
                FROM [LunchMatched]
        ) AS tmp
        WHERE (user1 = 'userEmail@company.com' OR user2 = 'userEmail@company.com')
    )

我也用 MIN() 进行了测试。没有结果。

SELECT MIN (userEmail)  as email
    FROM [LunchUsersAvailable]
    WHERE LunchGroupsID = '301' AND availableDate = '2015-01-01' AND userEmail != 'userEmail@company.com' EXCEPT
    (
        SELECT DISTINCT (COALESCE([user1], [user2])) AS matchedEmail
        FROM
        (
                SELECT [user1], [user2]
                FROM [LunchMatched]
            UNION ALL
                SELECT [user2], [user1]
                FROM [LunchMatched]
        ) AS tmp
        WHERE (user1 = 'userEmail@company.com' OR user2 = 'userEmail@company.com')
    )

为了以防万一,我使用 MAX() 进行了测试。 有效!

SELECT MAX (userEmail)  as email
    FROM [LunchUsersAvailable]
    WHERE LunchGroupsID = '301' AND availableDate = '2015-01-01' AND userEmail != 'userEmail@company.com' EXCEPT
    (
        SELECT DISTINCT (COALESCE([user1], [user2])) AS matchedEmail
        FROM
        (
                SELECT [user1], [user2]
                FROM [LunchMatched]
            UNION ALL
                SELECT [user2], [user1]
                FROM [LunchMatched]
        ) AS tmp
        WHERE (user1 = 'userEmail@company.com' OR user2 = 'userEmail@company.com')
    )

输出:

test4@company.com


总之,我的问题是:我如何获得 test2@company.com 作为结果?


更多测试更新: 我做了两个不同的测试。一个有效,另一个无效。我会详细展示每一个。首先,一切正常的测试:

我想分别解释每个 SELECT 以及每个部分的结果:

-- this outter SELECT lists all available users who are available
SELECT userEmail FROM [LunchUsersAvailable]
WHERE LunchGroupsID = '301' AND availableDate = '2015-01-01' AND userEmail != 'userEmail@company.com'

输出:

test1@company.com

test2@company.com

test3@company.com

test4@company.com

(受影响的 4 行)


-- this inner SELECT lists all the users who has been paired with userEmail@company.com before
SELECT DISTINCT (COALESCE([user1], [user2])) AS matchedEmail
FROM
(
        SELECT [user1], [user2]
        FROM [LunchMatched]
    UNION ALL
        SELECT [user2], [user1]
        FROM [LunchMatched]
) AS tmp
WHERE (user1 = 'userEmail@company.com' OR user2 = 'userEmail@company.com')

输出:

userEmail@company.com

test2@company.com

(2 行受影响)


运行完整代码,没有 TOP 1、MIN() 或 MAX():

test1@company.com

test3@company.com

test4@company.com

(受影响的 3 行)


到目前为止,这都是正确的。现在我只需要获取 test1@company.com 部分,因此我将代码的第一行从 从 [LunchUsersAvailable] 中选择用户电子邮件 到 从 [LunchUsersAvailable] 中选择前 1 个用户电子邮件

结果是:

test1@company.com

(受影响的 1 行)


这工作绝对很好。

但是现在,如果 userEmail@company.com 之前已经与 test1@company.com 而不是 test2@company.com 匹配([LunchMatched]表)结果不同。

INNER、OUTTER 和 FULL 代码(没有 TOP 1、MIN 或 MAX)的工作方式与上述相同,这很好。

但是,如果我将代码的第一行从 从 [LunchUsersAvailable] 中选择用户电子邮件 到 从 [LunchUsersAvailable] 中选择前 1 个用户电子邮件

结果是 null 而不是正确的: test2@company.com

(受影响的 1 行)

【问题讨论】:

  • 附注:您的查询过于复杂。首先,您选择所有 user1-user2 对以及所有 user2-user1 对。然后你删除所有没有合作伙伴是'userEmail\@company.com'的对然后你选择每对的第一个元素,包括删除空值,然后你使用不同的。而您实际上所做的只是:获取所有 user1 和所有 user2 的对话,其中一个合作伙伴是 'userEmail\@company.com'。在这里可以使用一个相当简单的 NOT EXISTS 子句。
  • [LunchMatched] 表存储了之前匹配的对(user1user2)。但它可以节省 userEmail@company.com | userB@company.comuserB@company.com | userEmail@company.com。我的代码试图在 [LunchMatched] 表上找到所有与 userEmail@company.com 匹配的人。这就是为什么我需要先做一个 UNION ALL (从双方得到这对)。 SELECT userEmail FROM [LunchUsersAvailable] 列出了所有可用用户,但我不想将 userEmail@company.com 与同一对配对,这就是我使用 EXCEPT 'old pair' 的原因。跨度>

标签: sql max min


【解决方案1】:

这表明userEmail 具有多个值,其中一个是空字符串(''——不是NULL)。

userEmail 返回的第一行有这个空值。 min() 会捕获它。 max() 没有。

检查userEmail <> '' 或只使用max()

如果您想要min(),您还可以使用带有case 的条件聚合:

select min(case when userEmail > '' then userEmail end)

编辑:

我看到了问题。 top 1min()max() 指的是except 之前的第一个查询。我错过了except,因为它被滚动到了行尾。 exceptunion 类似,将查询连接在一起。它不是where 子句的一部分。

【讨论】:

  • 您的建议在理论上似乎可行,但对我的问题无效。运行标准 SELECT userEmail 查询时,输出显示 (3 行受影响),这意味着没有空结果。
  • 我尝试运行SELECT MIN(case when userEmail > '' then userEmail end) as email 建议,但结果仍然(0 行受影响)
  • @criscmaia:如果除了 MIN(userEmail) 而不是 userEmail 之外的语句完全相同,并且您没有返回任何行,那么您的 dbms 中确实存在错误。
  • 这就是我害怕@ThorstenKettner 的原因。知道如何调试它吗?如果我运行 only 内部 SELECT DISTINCT[...] 它会返回正确的结果。外部 SELECT 在没有 EXCEPT 部分的情况下也可以正常工作。两者一起工作也很好。它也适用于 MAX(),但不适用于 MIN() 或 TOP 1。
【解决方案2】:

我明白了! TOP 1 或 MIN 正在 EXCEPT 之前处理,这意味着它将首先从可用用户结果中获取 TOP 1,并且只有他们应用 EXCEPTION。

解决方案:获取可用用户的完整列表,运行异常,然后才能获得 TOP 1

 SELECT TOP 1 useremail FROM (
    SELECT userEmail
    FROM [LunchUsersAvailable]
    WHERE LunchGroupsID = '301' AND availableDate = '2015-01-01' AND userEmail != 'userEmail@company.com' EXCEPT        -- from a specific cafeteria and date, EXCEPT
    (
        SELECT DISTINCT (COALESCE([user1], [user2])) AS matchedEmail                                                                        -- select all matched users in one column
        FROM
        (
                SELECT [user1], [user2]
                FROM [LunchMatched]
            UNION ALL
                SELECT [user2], [user1]
                FROM [LunchMatched]
        ) AS tmp
        WHERE (user1 = 'userEmail@company.com' OR user2 = 'userEmail@company.com')                                                  -- who has been previously matched with the user who is registering now
    )
) as tmp

【讨论】:

  • 是的,就是这样。愚蠢的我没有发现这一点。请记住,只要您不使用 ORDER BY 指定排序顺序,TOP 1 就会为您提供相当随机的记录。
【解决方案3】:

这不是答案。这只是为了展示如何简化原始查询。以下是多种可能性中的三种:

SELECT userEmail
FROM LunchUsersAvailable
WHERE LunchGroupsID = '301' 
AND availableDate = '2015-01-01' 
AND userEmail != 'userEmail@company.com' 
EXCEPT
(
  SELECT user1
  FROM LunchMatched 
  WHERE user2 = 'userEmail@company.com'
  UNION ALL
  SELECT user2
  FROM LunchMatched 
  WHERE user1 = 'userEmail@company.com'
);

或者:

SELECT userEmail
FROM LunchUsersAvailable
WHERE LunchGroupsID = '301' 
AND availableDate = '2015-01-01' 
AND userEmail != 'userEmail@company.com' 
AND userEmail NOT IN
(
  SELECT user1
  FROM LunchMatched 
  WHERE user2 = 'userEmail@company.com'
)
AND userEmail NOT IN
(
  SELECT user2
  FROM LunchMatched 
  WHERE user1 = 'userEmail@company.com'
);

或者:

SELECT userEmail
FROM LunchUsersAvailable
WHERE LunchGroupsID = '301' 
AND availableDate = '2015-01-01' 
AND userEmail != 'userEmail@company.com' 
AND NOT EXISTS
(
  SELECT *
  FROM LunchMatched 
  WHERE LunchUsersAvailable.userEmail IN (LunchMatched.user1, LunchMatched.user2)
  AND 'userEmail@company.com' IN (LunchMatched.user1, LunchMatched.user2)
);

在第二个和第三个查询中,可以轻松地将 userEmail 替换为 MIN(userEmail)。在第一个中,这将通过SELECT MIN(userEmail) FROM (first query here) 完成。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-31
    • 1970-01-01
    • 2017-07-25
    相关资源
    最近更新 更多