SQL-基于多行按条件选择

SQL-基于多行按条件选择,sql,Sql,我有两张桌子: actions action_data action\u data属于actions,并具有以下列:action\u id,name,value 内容可能如下所示: SELECT DISTINCT actions.* FROM actions INNER JOIN action_data ON actions.id = action_data.action_id WHERE (action_data.name = 'planet'

我有两张桌子:

actions 
action_data
action\u data
属于actions,并具有以下列:
action\u id
name
value

内容可能如下所示:

SELECT DISTINCT 
    actions.* 
FROM 
    actions 
INNER JOIN 
    action_data ON actions.id = action_data.action_id 
WHERE 
    (action_data.name = 'planet' AND action_data.value = 'earth');
    declare @t TABLE(id int, name NVARCHAR(MAX), value NVARCHAR(MAX))

    INSERT INTO @t VALUES(1, 'planet', 'earth')
    INSERT INTO @t VALUES(1, 'object', 'spaceship_a')
    INSERT INTO @t VALUES(1, 'destination', 'mars')

    SELECT * FROM @t t1
    JOIN @t t2 ON t1.ID = t2.id
    WHERE t1.name = 'planet' AND t1.value = 'earth' 
    AND t2.name = 'object' AND t2.value = 'spaceship_a'
SELECT DISTINCT action_id
FROM action_data a1 JOIN action_data a2 USING(action_id)
WHERE 
    a1.name = 'planet' AND a1.value = 'earth' AND
    a2.name = 'object' AND a2.value = 'spaceship_a';
操作

id | 
-----
178| 
179|
操作\u数据

action_id |   name   | value
-------------------------------------
178       |  planet  | earth
178       |  object  | spaceship_a
179       |  planet  | earth
179       |  object  | building
现在我想选择动作,动作中有
planet=earth和object=spaceship\u a
数据

如何使用SQL实现这一点?如果您只有一个条件,它的工作方式如下:

SELECT DISTINCT 
    actions.* 
FROM 
    actions 
INNER JOIN 
    action_data ON actions.id = action_data.action_id 
WHERE 
    (action_data.name = 'planet' AND action_data.value = 'earth');
    declare @t TABLE(id int, name NVARCHAR(MAX), value NVARCHAR(MAX))

    INSERT INTO @t VALUES(1, 'planet', 'earth')
    INSERT INTO @t VALUES(1, 'object', 'spaceship_a')
    INSERT INTO @t VALUES(1, 'destination', 'mars')

    SELECT * FROM @t t1
    JOIN @t t2 ON t1.ID = t2.id
    WHERE t1.name = 'planet' AND t1.value = 'earth' 
    AND t2.name = 'object' AND t2.value = 'spaceship_a'
SELECT DISTINCT action_id
FROM action_data a1 JOIN action_data a2 USING(action_id)
WHERE 
    a1.name = 'planet' AND a1.value = 'earth' AND
    a2.name = 'object' AND a2.value = 'spaceship_a';
但是我需要来自
操作\u数据的两个或多个条件


有什么想法吗?

我使用
分组方法和
拥有方法来解决这些问题,因为这是一种非常通用的方法,适用于许多情况

就你而言:

select ad.action_id
from action_data ad
group by ad.action_id
having sum(case when name = 'planet' and value = 'earth' then 1 else 0 end) > 0 and
       sum(case when name = 'object' and value = 'spaceship_a' then 1 else 0 end) > 0;
having
子句中的每个条件统计匹配的行数。
>0
表示至少有一个


您可以
join
返回到
actions
表,以获取更多列(如果需要)。

如果条件数量不变,可以使用join,这将比使用总和和大小写分组快得多

如果有两个条件,您可以这样加入:

SELECT DISTINCT 
    actions.* 
FROM 
    actions 
INNER JOIN 
    action_data ON actions.id = action_data.action_id 
WHERE 
    (action_data.name = 'planet' AND action_data.value = 'earth');
    declare @t TABLE(id int, name NVARCHAR(MAX), value NVARCHAR(MAX))

    INSERT INTO @t VALUES(1, 'planet', 'earth')
    INSERT INTO @t VALUES(1, 'object', 'spaceship_a')
    INSERT INTO @t VALUES(1, 'destination', 'mars')

    SELECT * FROM @t t1
    JOIN @t t2 ON t1.ID = t2.id
    WHERE t1.name = 'planet' AND t1.value = 'earth' 
    AND t2.name = 'object' AND t2.value = 'spaceship_a'
SELECT DISTINCT action_id
FROM action_data a1 JOIN action_data a2 USING(action_id)
WHERE 
    a1.name = 'planet' AND a1.value = 'earth' AND
    a2.name = 'object' AND a2.value = 'spaceship_a';
当然,如果您有3个条件,则需要加入2次并添加新筛选器:

    SELECT * FROM @t t1
    JOIN @t t2 ON t1.ID = t2.id
    JOIN @t t3 ON t1.ID = t3.id
    WHERE t1.name = 'planet' AND t1.value = 'earth' 
    AND t2.name = 'object' AND t2.value = 'spaceship_a'
    AND t3.name = 'destination' AND t3.value = 'mars'

如果不需要特定于DBMS的语法,可以使用自动联接

我会这样做:

SELECT DISTINCT 
    actions.* 
FROM 
    actions 
INNER JOIN 
    action_data ON actions.id = action_data.action_id 
WHERE 
    (action_data.name = 'planet' AND action_data.value = 'earth');
    declare @t TABLE(id int, name NVARCHAR(MAX), value NVARCHAR(MAX))

    INSERT INTO @t VALUES(1, 'planet', 'earth')
    INSERT INTO @t VALUES(1, 'object', 'spaceship_a')
    INSERT INTO @t VALUES(1, 'destination', 'mars')

    SELECT * FROM @t t1
    JOIN @t t2 ON t1.ID = t2.id
    WHERE t1.name = 'planet' AND t1.value = 'earth' 
    AND t2.name = 'object' AND t2.value = 'spaceship_a'
SELECT DISTINCT action_id
FROM action_data a1 JOIN action_data a2 USING(action_id)
WHERE 
    a1.name = 'planet' AND a1.value = 'earth' AND
    a2.name = 'object' AND a2.value = 'spaceship_a';
这适用于2个条件,但可以扩展到3个或更多,使用
FROM
子句中数据表的多个副本以及相应的比较条件

在这种情况下,
a1
复制品用于第一种情况(行星-地球),而
a2
复制品用于第二种情况(物体-宇宙飞船a)

JOIN
允许我们在所有可能的组合中搜索匹配项(N行给出N^2个组合)

这可能不是最好、最有效的方法,但它是可靠的,不依赖于平台

演示如下:

mysql> select * from action_data;
+-----------+--------+-------------+
| action_id | name   | value       |
+-----------+--------+-------------+
|       178 | planet | earth       |
|       178 | object | spaceship_a |
|       179 | planet | earth       |
|       179 | object | building    |
+-----------+--------+-------------+
4 rows in set (0.02 sec)

mysql> SELECT DISTINCT action_id
    -> FROM action_data a1 JOIN action_data a2 USING (action_id)
    -> WHERE 
    ->     a1.name = 'planet' AND a1.value = 'earth' AND
    ->     a2.name = 'object' AND a2.value = 'spaceship_a';
+-----------+
| action_id |
+-----------+
|       178 |
+-----------+
1 row in set (0.00 sec)
还有一些选择:

1) 使用
存在

select *
from actions a
where exists (select 1 from action_data ad 
    where ad.action_id = a.id and ad.name = 'planet' and ad.value = 'earth')
and exists (select 1 from action_data ad 
    where ad.action_id = a.id and ad.name = 'object' and ad.value = 'spaceship_a');
2) 将

with q1 as (
    select action_id
    from action_data
    where name = 'planet' and value = 'earth'
),
q2 as (
    select action_id
    from action_data
    where name = 'object' and value = 'spaceship_a'
)
select *
from q1 inner join q2 on q1.action_id = q2.action_id;

因为您不知道要搜索的元数据的数量,所以我不建议使用未知/无限数量的
连接

改为使用组串联

select * from actions 
  join (
    select action_id,
      group_concat(name,'=',value order by name separator ',') as csv // MySQL
//    string_agg(name || '=' || value, ',' order by name) as csv // PostgreSQL
    from meta 
    where name in ('planet', 'object') 
    group by action_id
  ) meta 
  on actions.id = meta.action_id 
  where csv = 'object=building,planet=earth'

我很高兴听到SQL关于性能的建议,我想,如果要找到3+以上的值,性能会更好。

数据透视表会工作吗?以什么方式工作?我认为数据透视表不起作用如果你提前知道你的“名字”,数据透视表最终会看起来像一个普通的表:where planet=earth and object=spaceship\u a你在用哪个数据库管理系统?博士后?神谕