2015-07-01 48 views
2

我一直在爲此奮鬥了一段時間。我有一個SQLFiddle with the same approximate contents of this question.在沒有鏈接的情況下減少CROSS JOIN + LEFT JOIN的行數

我有三個表,itemsprofiles,並鏈接表,posts來自前兩個表鍵,模式帶有示例數據:

create table items ( 
    item_id int unsigned primary key auto_increment, 
    title varchar(255) 
); 

insert into items (item_id, title) VALUES(1, 'Item One'); 
insert into items (item_id, title) VALUES(2, 'Item Two'); 
insert into items (item_id, title) VALUES(3, 'Item Three'); 
insert into items (item_id, title) VALUES(4, 'Item Four'); 
insert into items (item_id, title) VALUES(5, 'Item Five'); 

create table profiles (
    profile_id int unsigned primary key auto_increment, 
    profile_name varchar(255) 
); 

insert into profiles (profile_id, profile_name) VALUES(1, 'Bob'); 
insert into profiles (profile_id, profile_name) VALUES(1, 'Mark'); 
insert into profiles (profile_id, profile_name) VALUES(1, 'Nancy'); 

create table posts (
    post_id int unsigned primary key auto_increment, 
    item_id int unsigned, -- Relates to items.item_id 
    profile_id int unsigned, -- Relates to profile.profile_id, 
    post_date DATETIME 
); 

insert into posts (item_id, profile_id, post_date) values(1, 1, NOW()); 
insert into posts (item_id, profile_id, post_date) values(2, 2, NOW()); 
insert into posts (item_id, profile_id, post_date) values(2, 2, NOW()); 

我使用下面的查詢生產近乎正確的結果:

SELECT 
    `items`.`item_id`, 
    `items`.`title`, 
    `profiles`.`profile_id`, 
    `profiles`.`profile_name`, 
    `posts`.`post_id`, 
    `posts`.`post_date` 
    FROM `items` 
    CROSS JOIN `profiles` 
    LEFT JOIN `posts` ON `items`.`item_id` = `posts`.`item_id` 
    AND `posts`.`profile_id` = `profiles`.`profile_id`; 

對於我的具體應用,這是次優的。我得到了很多額外的行,我的特殊實現不需要。最終的結果看起來是這樣的:

+------------|------------|---------|-----------+ 
| Item Name | Profile ID | Post ID | Post Date | 
+------------+------------+---------+-----------+ 
| Item One | 1   | 1  | 2015-... | -- Bob Posted this 
| Item One | 2   | NULL | NULL  | -- No one else did 
| Item One | 3   | NULL | NULL  | 
| Item Two | 1   | 2  | 2015-... | -- Bob posted this 
| Item Two | 2   | 3  | 2015-... | -- So did mark 
| Item Two | 3   | NULL | NULL  | -- Nancy didn't 
| Item Three | 1   | NULL | NULL  | 
| Item Three | 2   | NULL | NULL  | 
| Item Three | 3   | 4  | 2015-... | -- Only nancy posted #3 
| Item Four | 1   | NULL | NULL  | -- No one posted #4 
| Item Four | 2   | NULL | NULL  | 
| Item Four | 3   | NULL | NULL  | 
| Item Five | 1   | NULL | NULL  | -- No one posted #5 
| Item Five | 2   | NULL | NULL  | 
| Item Five | 3   | NULL | NULL  | 
+------------+------------+---------+-----------+ 

這是做正是我要求 - 每個項目返回三次(對應於輪廓數)。然而這將是理想的,如果在項#4和如下#5,其中沒有任何聯繫,它們僅被返回一次,用NULL PROFILE_ID的情況下:

+------------|------------|---------|-----------+ 
| Item Name | Profile ID | Post ID | Post Date | 
+------------+------------+---------+-----------+ 
| Item One | 1   | 1  | 2015-... | -- Bob Posted this 
| Item One | 2   | NULL | NULL  | -- No one else did 
| Item One | 3   | NULL | NULL  | 
| Item Two | 1   | 2  | 2015-... | -- Bob posted this 
| Item Two | 2   | 3  | 2015-... | -- So did mark 
| Item Two | 3   | NULL | NULL  | -- Nancy didn't 
| Item Three | 1   | NULL | NULL  | 
| Item Three | 2   | NULL | NULL  | 
| Item Three | 3   | 4  | 2015-... | -- Nancy posted #3 
| Item Four | NULL  | NULL | NULL  | -- **No one posted #3 and #4 
| Item Five | NULL  | NULL | NULL  | -- Only need #3 and #4 once** 
+------------+------------+---------+-----------+ 

儘管在該示例這隻能減少4行,在我的實際應用程序中,有很多項目,但沒有多少配置文件和帖子。所以這個小小的改變可以顯着減少服務器端語言處理。

任何人都可以指出我在正確的方向限制交叉連接只有我有某種類型的鏈接?

+0

寫得很好的問題,有腳本和小提琴。所有的都是A.B.卡羅爾呢! – Quassnoi

回答

2
SELECT `items`.`item_id`, 
     `items`.`title`, 
     `profiles`.`profile_id`, 
     `profiles`.`profile_name`, 
     `posts`.`post_id`, 
     `posts`.`post_date` 
FROM `items` 
LEFT JOIN 
     `profiles` 
ON  EXISTS 
     (
     SELECT NULL 
     FROM `posts` 
     WHERE `posts`.`item_id` = `items`.`item_id` 
     ) 
LEFT JOIN 
     `posts` 
ON  `items`.`item_id` = `posts`.`item_id` 
     AND `posts`.`profile_id` = `profiles`.`profile_id` 
ORDER BY 
     `items`.`item_id`, `profiles`.`profile_id` 

http://sqlfiddle.com/#!9/c81b1/41

+0

巧妙的解決方案 – Barmar

+0

我真的很好奇它是如何工作的,如果你能稍微擴展一下,那會很棒,但除此之外,我被吹走了,真誠的謝謝你! –

+0

@ A.B.Carroll:你不會對配置文件進行交叉連接,而只是根據'items'做一個true或false條件的左連接。這個條件是「那個項目上有帖子」嗎?如果存在,則條件爲真,並且所有配置文件都按照交叉連接一樣進行選擇;如果不存在,則條件爲false,並從'profiles'中選擇一個NULL記錄。其餘的都是一樣的。 – Quassnoi