2

编辑:

我已经包含了创建语句和一小组测试数据供您试用。因此,我将示例更改为id2而不是5表示id测试数据中存在的一个。

/ 编辑

我有三个 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对特定语言环境不可用时,它会自动回退到该语言环境的根页面(即: where page.parentId IS NULL)?我的目标是让 MySQL 给我一个,但不是两个,用于活动的语言环境。

我试过了:

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

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

4

3 回答 3

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
于 2011-02-14T01:45:55.003 回答
0

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

在 SELECT 列表中使用 IFNULL。第一个参数用于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')
于 2011-02-14T00:13:53.187 回答
0

异或呢?“一个或另一个,但不是两者”:

WHERE
    (p.id = '5' XOR (p.parentId IS NULL))
                ^---here
于 2011-02-13T17:19:17.543 回答