MySQL:如果其他行不可用,则选择默认行(不是默认值)

时间:2011-02-13 13:07:35

标签: sql mysql join default fallback

修改

我已经包含了create语句和一小组测试数据供您试用。因此,我已将示例id更改为2而不是5,以表示测试数据中的现有id

/ edit

我有三个MySQL表来保存本地化页面信息:

CREATE TABLE `locale` (
  `languageCode` char(3) NOT NULL,
  `regionCode` char(3) NOT NULL default 'ZZ',
  `isActive` enum('yes','no') NOT NULL default 'no',
  PRIMARY KEY  (`languageCode`,`regionCode`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `page` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `parentId` int(10) unsigned default NULL,
  PRIMARY KEY  (`id`),
  KEY `FK_page_page_1` (`parentId`),
  CONSTRAINT `FK_page_page_1` FOREIGN KEY (`parentId`) REFERENCES `page` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `pageInfo` (
  `pageId` int(10) unsigned NOT NULL,
  `languageCode` char(3) NOT NULL,
  `regionCode` char(3) NOT NULL default 'ZZ',
  PRIMARY KEY  (`pageId`,`languageCode`,`regionCode`),
  KEY `FK_pageInfo_locale_1` (`languageCode`,`regionCode`),
  KEY `FK_pageInfo_page_1` (`pageId`),
  CONSTRAINT `FK_pageInfo_locale_1` FOREIGN KEY (`languageCode`, `regionCode`) REFERENCES `locale` (`languageCode`, `regionCode`) ON DELETE CASCADE,
  CONSTRAINT `FK_pageInfo_page_1` FOREIGN KEY (`pageId`) REFERENCES `page` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

以下是一些测试数据:

/* locale */
INSERT INTO `locale` (languageCode,regionCode,isActive) VALUES ('de','ZZ','yes');
INSERT INTO `locale` (languageCode,regionCode,isActive) VALUES ('en','ZZ','yes');
INSERT INTO `locale` (languageCode,regionCode,isActive) VALUES ('nl','ZZ','yes');
/* page */
INSERT INTO `page` (id,parentId) VALUES (1,NULL);
INSERT INTO `page` (id,parentId) VALUES (2,1);
/* pageInfo */
INSERT INTO `pageInfo` (pageId,languageCode,regionCode) VALUES (1,'de','ZZ');
INSERT INTO `pageInfo` (pageId,languageCode,regionCode) VALUES (1,'en','ZZ');
INSERT INTO `pageInfo` (pageId,languageCode,regionCode) VALUES (1,'nl','ZZ');
INSERT INTO `pageInfo` (pageId,languageCode,regionCode) VALUES (2,'en','ZZ');
INSERT INTO `pageInfo` (pageId,languageCode,regionCode) VALUES (2,'nl','ZZ');

困境:
要检索具有id 2所有活动语言环境的页面,请发出以下SQL语句:

SELECT 
        p.*,   pi.languageCode,   pi.regionCode
    FROM
        page AS p
        INNER JOIN pageInfo AS pi
            ON pi.pageId = p.id
        INNER JOIN locale AS l
            ON l.languageCode = pi.languageCode AND l.regionCode = pi.regionCode
    WHERE
        (p.id = '2')
        AND (l.isActive = 'yes')

如何更改此语句,以便当id 2不可用于特定区域设置时,它会自动回退到该区域设置的根页面(即:page.parentId IS NULL)?我的目标是让MySQL为活动区域设置提供一个,但不是两个。

我试过了:

WHERE
    (p.id = '2' OR (p.parentId IS NULL))

但是,当然,这给了我两个实际上也有id 2的语言环境记录。我很确定它是可能的(使用UNION,子选择或页面上的重复连接)但是我在这里有一个完整的SQL编写器块。我很感激你的帮助。

3 个答案:

答案 0 :(得分:0)

XOR怎么样? “一个或另一个但不是两个”:

WHERE
    (p.id = '5' XOR (p.parentId IS NULL))
                ^---here

答案 1 :(得分:0)

使用LEFT JOIN并将两个表连接两次。第二次加入专门针对p.parentId IS NULL的情况。

在SELECT列表中使用IFNULLs。第一个参数用于指定的id,第二个参数是回退,即根页的值。

SELECT
    p.*,
    IFNULL(l.languageCode, lr.LanguageCode) AS languageCode,
    IFNULL(l.regionCode, lr.regionCode) AS regionCode
FROM
    page AS p
    LEFT JOIN pageInfo AS pi
        ON pi.pageId = p.id
    LEFT JOIN locale AS l
        ON l.languageCode = pi.languageCode AND l.regionCode = pi.regionCode
    LEFT JOIN pageInfo AS pir
        ON pir.pageId = p.id AND p.parentId IS NULL
    LEFT JOIN locale AS lr
        ON lr.languageCode = pir.languageCode AND lr.regionCode = pir.regionCode
WHERE
    (p.id = '5' AND l.isActive = 'yes')
    OR (p.parentId IS NULL AND lr.isActive = 'yes')

答案 2 :(得分:0)

您可以通过p.id = 5订购并使用限制为1.这有点像黑客,但它会起作用。

SELECT 
        p.*,   pi.languageCode,   pi.regionCode
    FROM
        page AS p
        INNER JOIN pageInfo AS pi
            ON pi.pageId = p.id
        INNER JOIN locale AS l
            ON l.languageCode = pi.languageCode AND l.regionCode = pi.regionCode
    WHERE
        (p.id = '5' OR (p.parentId IS NULL))
        AND (l.isActive = 'yes')
    ORDER BY p.id = '5' DESC
    LIMIT 1