2013-10-15 51 views
1

我遇到了一個問題,我不知道是否有任何方法僅通過SQL的幫助來解決它。 我有一個表users具有特殊修改的SQL選擇查詢

+----+------------+ 
| ID | group_id | 
+----+------------+ 
| 1 | 1;2;3 | 
+----+------------+ 
| 2 | 1;2;3;4 | 
+-----------------+ 
| 3 | 1;2;3;6 | 
+-----------------+ 
| 4 | 1;2;6 | 
+-----------------+ 

我知道,這不是正常的形式,但我不能改變它,因爲它已經在客戶端使用。

所以問題是不顯示用戶有我沒有的組。

例如:如果我爲ID = 3的用戶進行選擇,則需要顯示用戶1,3和4,因爲用戶ID = 3具有用戶ID = 1的所有組,但我不應顯示用戶ID = 2

我的同事幫我找到最佳的解決方案,如果有人有興趣:

SELECT * 
FROM `users` 
WHERE CONCAT('[',REPLACE('1;2;3;6', ';', ']['), ']') LIKE CONCAT('%[', REPLACE(group_id,';',']%['),']%') 
+0

會更好地告訴客戶會有DB結構的下一個版本的變化。 –

+0

不幸的是,這是不可能的。整個結構應該改變,因爲都是這樣的,客戶肯定會說不。 –

回答

1

試試這個:

SELECT * 
FROM users u 
WHERE 
INSTR(
    (SELECT group_id 
    FROM users u2 
    WHERE u2.id = '3'), 
u.group_id) <> 0 

顯示SqlFiddle

**編輯:**

必須實現每行一個存儲過程改變你的組ID字符串在一個新的臨時表的行。所以,你有這樣的情況:

TEMP_TABLE(USER_ID,GROUP_ID)

user_id group_id 
1   1 
1   2 
1   3 
2   1 
2   2 
2   3 
2   4 

所以,你的查詢可以以這種方式與一個NOT EXISTS條件改寫:(I」已經創建了應用表,但你必須改變我的應用程序表,存儲過程的輸出)

create table user (id int, group_id varchar(20)); 
insert into user values(1, '1;2;3'), 
(2, '1;2;3;4'), 
(3, '1;2;3;6'), 
(4, '1;2;6'); 

CREATE TABLE app (user_id int, group_id varchar(10)); 
insert into app values 
(1, '1'), 
(1, '2'), 
(1, '3') and so on 

select * 
from user u 
where id not in (
    select distinct a1.user_id 
    from app a1 
    where not exists(
    select 'x' 
    from app a2 
    where a2.user_id = 3 
    and a2.group_id = a1.group_id 
) 
) 

顯示新SqlFiddle

+0

這也將返回使用誰有組13,例如..不知道這個問題是否與OP有關,但。 – Mureinik

+0

@Mureinik:我已經解決了我的問題。沒有第13組是不同的。每個組ID都被相隔; –

+0

@Joe Taras - 這不是那麼容易,我已經更新了我的帖子,請檢查它。 –

0

正如你所說,你不能改變它,但對於這些情況下,一個簡單的解決方案是按位:

http://dev.mysql.com/doc/refman/5.0/en/bit-functions.html

如:

我設置了多個方案的狀態,如:
1 =潛在客戶
2 =取消服務
4 =池服務
8 =割草
16 =洗窗
32 =修剪樹木
64 =油漆
128 =移動換油

有了這個方案的客戶誰需要池服務(4)和洗窗(16)將有20的狀態( 4 + 16)。可能對移動機油更換(128)感興趣的潛在客戶(1)的狀態爲129(1 + 128)。等等...

按位邏輯讓我選擇誰買根據他們的狀態值尤其是服務客戶。

SELECT * FROM customers WHERE status & 1 
//returns all prospects) 

SELECT * FROM customers WHERE status & 16 
//returns all of my window washing customers even if they are cancelled 

SELECT * FROM customers WHERE (status & 16) AND !(status & 2) 
//returns all of my window washing customers but not the cancelled ones 

SELECT * FROM customers WHERE status & 36 
//returns all of my tree trimming AND pool service customers i.e. 32+4 

SELECT * FROM customers WHERE status & 10 
//returns all cancelled lawn moving customers i.e. 2+8 

SELECT * FROM customers WHERE (status & 32) AND !(status & 128) 
//returns all tree trimming customers who do not get a mobile oil change 
+0

表用戶非常大,表與組甚至大,所以我不認爲最好的方法是更新用戶(如果我理解你的權利,你想添加新的列,我的應該存儲狀態)。 –

+0

這更多的建議:對於每個組,創建一個字段來保存一個字節數(1,2,4,8 ...)。總結所有「字節數」並將結果保存到「group_id」字段中(而不是用;分隔),並使用上面的選擇來檢索組合。 – VancleiP

+0

問題是,我不僅使用這張表,而且如果我改變一些東西會帶來很多問題。 –

1

MySQL對這些用例有一個內置命令,find_in_set。 不幸的是,它只用逗號分隔值的作品,而不是由分號分隔的值,但也可以很容易地克服:

SELECT * 
FROM users 
WHERE FIND_IN_SET('3', REPLACE(group_id,';',',')) 

SQLFiddle

+0

這幾乎是正確的,但不完全正確。例如,如果我需要爲用戶ID = 1進行選擇,那麼它應該只選擇ID = 1的用戶,因爲其他用戶有其他用戶 –

+0

這就是您在OP中要求的內容 - 「只顯示擁有我擁有的一些組或所有組的用戶。「請澄清。 – Mureinik

+0

對不起,我的錯。正確的變體是:不顯示有我沒有的組的用戶 –

0

我創建「一個班輪」,也許這是有點複雜,但作爲加號,不需要使用存儲過程或臨時表。

減號是select 1 union select 2 union select 3 union...的一部分,因爲我們需要一個人創造儘可能多的行儘可能羣體。

where id1 = 3是參數。

create table users (id integer, group_id varchar(100)); 


insert into users 
select 1, '1;2;3' union 
select 2, '1;2;3;4' union 
select 3, '1;2;3;6' union 
select 4, '1;2;6'; 


select id2 as user_id 
from (
    select u1.id as id1, u2.id as id2, count(*) as qty 
    from (
     select distinct id, group_id, substring_index(substring_index(group_id, ';', t.n), ';', -1) as g_id 
     from users 
     cross join (
      select @i := @i + 1 as n from (select @i:=0) t0, 
      (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t1, 
      (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t2 
     ) as t 
    ) as u1 
    join users u2 on find_in_set(u1.g_id, replace(u2.group_id, ';', ',')) > 0 
    group by u1.id, u2.id 
) as t 

join (
    select id, group_id, length(group_id) - length(replace(group_id, ';', '')) + 1 as groups_count 
    from users 
) as g on t.id2 = g.id and g.groups_count <= t.qty 
where id1 = 3; 

Live example on SQL Fiddle

如果需要超過100組(每人)添加另一條線(使用不同的別名):

(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) txxxx 

我檢查了它,它接縫確定:

  • 1倍的回報1
  • 爲2返回1,2
  • 3返回1,3,4
  • 爲4個返回4