2011-02-13 100 views
2

编辑:MySQL的:选择默认的行(不是默认值),如果其他行不提供

我已经包括了创建报表和一个小的测试数据集,为您尝试。因此,我已将示例id更改为2而不是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'); 

的困境:
用检索页面所有活动区域设置我发出以下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编写器块。我会很感激你的帮助。

+0

如果父页面也不适用于特定的语言环境,该怎么办? – 2011-02-13 13:16:14

+0

@ Scrum Meister:好问题!但是假设系统中始终可以使用根页面(parentId IS NULL,即不一定是父页面)。这是一个无法删除的页面。 – 2011-02-13 13:18:31

回答

0

XOR如何? “一个或另一个但不是两个”:

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

没有抱歉,如果带有`id 5`的页面可用于该语言环境,那么仍然会为每个语言环境提供两个记录。 – 2011-02-13 18:58:25

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') 
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