MySQL select...into variable 产生 null,即使数据在那里

spr*_*aff 2 mysql

我的存储过程执行SELECT ... INTO var产生 NULL,但如果我SELECT自己重复相同的操作,我会得到一个值。

这是架构的相关部分

CREATE TABLE UrlAuthority
(
     id              BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
    ,name            VARCHAR(255) NOT NULL COMMENT 'includes TLD suffix'
    ,UNIQUE(name)
)
ENGINE = INNODB DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;



CREATE TABLE UrlFqdn
(
     id              BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
    ,authority       BIGINT NOT NULL COMMENT 'references example.com'
    ,name            VARCHAR(255) NOT NULL COMMENT 'host.example.com'
    ,FOREIGN KEY (authority) REFERENCES UrlAuthority (id)
    ,UNIQUE (authority, name)
)
ENGINE = INNODB DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;



CREATE TABLE Url
(
     id              BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
    ,fqdn            BIGINT NOT NULL
    ,path            VARCHAR(255) NOT NULL
    ,FOREIGN KEY (fqdn) REFERENCES UrlFqdn (id)
    ,UNIQUE (fqdn, path)
)
ENGINE = INNODB DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
Run Code Online (Sandbox Code Playgroud)

这是程序

CREATE PROCEDURE UrlToId (url TEXT)
BEGIN
    DECLARE host      TEXT;
    DECLARE authority TEXT;
    DECLARE tld       TEXT;
    DECLARE pathtext  TEXT;

    DECLARE authority_tld TEXT;
    DECLARE fqdn          TEXT;
    DECLARE authority_id  BIGINT;
    DECLARE fqdn_id       BIGINT;
    DECLARE url_id        BIGINT;
    DECLARE err           TEXT;

    CALL UnpackURL (url, host, authority, tld, pathtext);

    SELECT JoinHostNames(authority, tld)      INTO authority_tld;
    SELECT JoinHostNames(host, authority_tld) INTO fqdn;

    INSERT IGNORE INTO UrlAuthority (name) VALUES (authority_tld);

    SELECT id
    INTO authority_id
    FROM UrlAuthority
    WHERE name=authority_tld;

    -- LOG ('UrlAuthority', authority_tld, authority_id)

    INSERT IGNORE INTO UrlFqdn (authority, name)
    VALUES (authority_id, fqdn);

    SELECT id
    INTO fqdn_id
    FROM UrlFqdn
    WHERE authority=authority_id AND name=fqdn;

    -- LOG ('UrlFqdn', fqdn, fqdn_id)

    INSERT IGNORE INTO Url (fqdn, path) VALUES (fqdn_id, pathtext);

    -- LOG ('Url', fqdn_id, pathtext)

    SELECT id FROM Url WHERE fqdn=fqdn_id AND path=pathtext;
END
|
Run Code Online (Sandbox Code Playgroud)

这些LOG行有点元编程,它们将字符串插入到调试表中。

如果我跑

call UrlToId('http://mail4.z.uk/foo/bar');
Run Code Online (Sandbox Code Playgroud)

我收到这些调试消息

UrlAuthority authority_tld=z.uk authority_id=1
UrlFqdn fqdn=mail4.z.uk fqdn_id=NULL
Url fqdn_id=NULL pathtext=/foo/bar
Run Code Online (Sandbox Code Playgroud)

显然,问题是,fqdn_id=NULL但是,如果我手动重复执行SELECT id INFO fqdn_id并替换跟踪中指示的变量值的查询:

    SELECT id
    -- INTO fqdn_id
    FROM UrlFqdn
    WHERE authority=1 /* authority_id */ AND name='mail4.z.uk' /* fqdn */;
Run Code Online (Sandbox Code Playgroud)

1将按预期进行选择。

select得到数据,为什么是变数NULL后来呢?

Mic*_*bot 5

有一段时间,这个问题让我望而却步。我不喜欢SELECT ... INTO,所以我真的想把你的麻烦归咎于它,但没有。

问题来了。

SELECT id
  INTO fqdn_id
  FROM UrlFqdn
 WHERE authority=authority_id AND name=fqdn;
Run Code Online (Sandbox Code Playgroud)

MySQL如何解释这个?

SELECT id
  INTO fqdn_id
  FROM UrlFqdn
 WHERE 'mail4' = 1 AND name = 'mail4.z.uk';
/* Impossible WHERE */
/* see DECLARE authority TEXT; */
Run Code Online (Sandbox Code Playgroud)

局部变量不应与表列同名。如果 SQL 语句(例如SELECT ... INTO语句)包含对列的引用和具有相同名称的声明的局部变量,则 MySQL 当前将引用解释为变量的名称。

—  http://dev.mysql.com/doc/refman/5.7/en/local-variable-scope.html

对变量和列名使用相同的标识符会使调试变得一团糟,不管怎样。