2011-02-13 66 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