如何确定WHERE子句上的哪个条件失败?

时间:2016-07-03 22:59:56

标签: mysql sql

我有一张这样的表:

// cookies
+----+---------+-------------------+------------+
| id | user_id |       token       |   expire   |
+----+---------+-------------------+------------+
| 1  | 32423   | dki3j4rf9u3e40... | 1467586386 |
| 2  | 65734   | erhj5473fv34gv... | 1467586521 |
| 3  | 21432   | 8u34ijf34t43gf... | 1467586640 |
+----+---------+-------------------+------------+

我有这个问题:

SELECT 1
FROM cookies
WHERE user_id = :id AND
      token   = :token

注意:结果始终是一个行或行。 (令牌列为unique

当有匹配的行时,确定,一切正常,但是当没有任何匹配的行时,我想了解原因?

  • user_id存在但token
  • token存在但user_id
  • 非这些列不存在

如何确定"没有选择(匹配)行的原因"

编辑:以下是所有可能的输出:

  1. 行存在
  2. 行不存在,因为user_id = :id false
  3. 行不存在,因为token = :token false
  4. 行不存在,因为user_id = :idtoken = :token都是 false
  5. 行不存在,因为user_id = :idtoken = :token true 但不在同一行。

3 个答案:

答案 0 :(得分:6)

这总是返回一行。如果匹配则匹配,否则为0.否则为0.它还显示cookie表中是否存在userID或令牌。使用此方法可以确定where子句失败的原因。

SELECT (  select count(*) > 0 matched from cookies where user_id = :id  and token   = :token ) matched
, ( select count(*) > 0 userIdExists from cookies where user_id = :id ) userIdExists
, ( select count(*) > 0 tokenExists from cookies where token = :token ) tokenExists

答案 1 :(得分:3)

前言:这个问题,尽管我不喜欢MySQL ,但却是一个难以解决的难题。没有使用FULL OUTER JOIN使它变得更加困难。即使是我最初给出的“解决方案”也不足以完成任务。

OUTER JOINS以特定模式运行,重要的是将每个内部查询视为独立于外部相对侧的显式方向。

SELECT A.Col_A, B.Col_B
TableA A
LEFT OUTER JOIN TableB B ON B.ID = A.ID

如果TableA具有与TableB匹配的内容,则此查询仅生成结果。因为每个嵌套查询在逻辑上与上面相同,所以即使添加一个虚拟表也只留下虚拟表的结果,我感觉像是...... dummy

虽然有些智能用户尝试使用UNION / UNION ALL来解决这个问题,但当我尝试两次使用同一个表时,这个答案是不可靠的并且实际上失败了。

  • 解决方案:FOR REAL !!

诀窍是保证结果永远返回。

SELECT C.user_id
     , B.token
FROM (SELECT NULL AS C) A
LEFT OUTER JOIN (SELECT token
                 FROM  Example
                 WHERE token = @token) AS B ON 1 = 1 
LEFT OUTER JOIN (SELECT user_id
                 FROM Example
                 WHERE user_id = @user_id) AS C ON 1 = 1
  • 我们使用DUMMY表来确保我们总是得到一行......即使它是空的。
  • 注意On 1 = 1。这保证了双方的结果将会回归,因为我们确保这些表格只检索了我们想要的确切信息,宾果游戏!无论是一方还是双方都是NULL,我们都会找到漂亮的解决方案。

重新审核原始程序

表声明

CREATE TABLE sys.EXAMPLE (ID INT auto_increment PRIMARY KEY NOT NULL
                    , user_id INT NULL
                    , token NVARCHAR(100) NULL
                    , `expire` INT NULL ); 
INSERT INTO sys.EXAMPLE (user_Id, token, `expire`)
VALUES  (32423, N'dki3j4rf9u3e40...', 1467586386)
      , (65734, N'erhj5473fv34gv...', 1467586521)
      , (21432, N'8u34ijf34t43gf...', 1467586640);

- 我的MySQL Workbench错误在开头,由于某种原因,变量会持续超出交易。

程序解决方案

-- DROP PROCEDURE sys.MyExample
DELIMITER $$
CREATE PROCEDURE sys.MyExample(IN user_ID INT, IN token NVARCHAR(255) )   
BEGIN

SELECT B.token, C.user_id INTO @token_chk, @user_chk
FROM (SELECT NULL AS C) A
LEFT OUTER JOIN (SELECT token
                FROM  sys.Example
                WHERE token = @token
                 LIMIT 1) AS B ON 1 = 1 
LEFT OUTER JOIN (SELECT user_id
                 FROM sys.Example
                 WHERE user_id = @user_id
                 LIMIT 1) AS C ON 1 = 1;

IF @user_chk IS NOT NULL AND @token_chk IS NOT NULL
-- RESULT_1: FOUND BOTH COLUMNS
THEN SELECT 'FOUND IT';
    ELSE IF @user_chk IS NOT NULL AND @token_chk IS NULL
-- RESULT_2: TOKEN IS MISSING
         THEN SELECT 'TOKEN IS MISSING';
         ELSE IF @user_chk IS NULL AND @token_chk IS NOT NULL
-- RESULT_3: USER_ID IS MISSING
                    THEN SELECT 'user_ID IS MISSING';
                    ELSE IF @user_chk IS NULL AND @token_chk IS NULL
-- RESULT_4: BOTH USER_ID AND TOKEN ARE MISSING
                          THEN SELECT 'BOTH user_id AND token are missing';
                          ELSE -- return message saying that an unknown error has occurred
                          SELECT 'AN UNKNOWN ERROR HAS OCCURRED';
                          END IF;
                    END IF;
            END IF;
    end if;
END
$$
DELIMITER ; 

请注意,这适用于T-SQL,在运行PROCEDURE之前在MySQL中运行。出于某种原因,我遇到了一个错误,即我的proc中的第二个变量在查询期间失去了它的值...即使我在过程中的查询之前显式调用了相同的值。

-- Should Return complete set
    CALL sys.MyExample(21432, 'erhj5473fv34gv...');
-- Should Return 'token is missing'
    CALL sys.MyExample(21432, 'erhj5473fv34gv..X'); 
-- Should Return 'user_id is missing'
    CALL sys.MyExample(2143, 'erhj5473fv34gv...'); 
-- Should Return 'Both user_id AND token are missing'
    CALL sys.MyExample(2143, 'erhj5473fv34gv..X');  

答案 2 :(得分:1)

没有办法(至少我知道)只从一个查询中获取此类信息,就像你在这里查询一样。您的数据库将使用您提供的组合标准运行查询,并得出结论:这些条件匹配或不匹配任何行。它不会打扰你自己的哪个标准使它匹配或不匹配任何行。

假设您正在使用这些查询作为某些服务器端页面逻辑(如PHP文件)的一部分,我会说三个查询是您可以执行此操作的最小值。

  1. 您已拥有的查询,只有在您正在查找的令牌和用户ID之间存在匹配时才会返回一行。

  2. 用于返回与令牌匹配的行数的查询。

  3. 用于返回与用户ID匹配的行数的查询。

  4. 如果查询#1返回一行,则当前不需要最后两行:满足选项#1,跳到后续代码。但如果没有,你将需要其他两个查询来确定其他四个可能选项中的哪一个反映了事物的状态:

    如果查询#3返回的行数超过零而查询#2返回零行,则用户ID存在,但令牌不满足选项#2。

    如果查询#2返回的行数超过零而查询#3返回零行,则该令牌存在,但用户ID不满足选项#3。

    如果两个查询都返回零行,则既不存在令牌也不存在用户ID - 选项#4满足。

    如果两个查询返回的行数都超过零,则存在令牌和用户ID,但由于查询#1没有返回任何行,因此它们不存在于同一行 - #5满足中。