1

我有一个问题,我不知道是否有任何方法可以仅借助 SQL 来解决它。我有一张桌子users

+----+------------+
| 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,';',']%['),']%')
4

4 回答 4

1

MySQL 有一个用于此类用例的内置命令,find_in_set. 不幸的是,它只适用于逗号分隔的值,而不是用分号分隔的值,但这也很容易克服:

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

SQLFiddle

于 2013-10-15T10:45:34.017 回答
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 条件重写:(我已经创建了 app 表,但您必须使用存储过程的输出更改我的 app 表)

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

于 2013-10-15T10:21:25.067 回答
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)。ETC...

按位逻辑允许我根据状态值选择购买特定服务的客户。

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
于 2013-10-15T10:24:05.753 回答
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;

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
于 2013-10-15T13:17:47.130 回答