MySQL 根据特定结果加入
MySQL joining on specific result
我正在尝试构建一个查询以从表单生成器中提取数据,在该表单生成器中我只能在另一个字段值等于特定值的情况下加入一个字段值。我的查询 returns 没有行。
users
------------------
|id | name |
------------------
|40 | John |
|45 | Michael |
|47 | Bob |
------------------
data_table
----------------------------------------------------
|id | submission | field_type | field_value |
----------------------------------------------------
|1 | 12345 | user | 40 |
|2 | 12345 | score | 5 |
|3 | 12345 | completed | 1 |
|4 | 23456 | user | 45 |
|5 | 23456 | score | 3 |
|6 | 23456 | completed | 0 |
|7 | 45678 | user | 47 |
|8 | 45678 | score | 2 |
|9 | 45678 | completed | 1 |
----------------------------------------------------
Desired result
---------------
|Name | Score |
---------------
|John | 5 |
|Bob | 2 |
---------------
Select
u.name,
dt2.field_value as score
from
users u
left join
data_table dt on u.id=dt.field_value and dt.field_type='user'
left join
data_table dt2 on dt.submission=dt2.submission and dt2.field_type='score'
where
(dt.field_type='completed' and dt.field_value=1)
你可以做类似的事情(如果你真的不能改变你的数据结构,它看起来......很奇怪)。
您将在 data_table 上有一个子查询,在 table 上有两个自连接(因为您需要 3 个不同的行和条件)
select u.name, s.score
from users u
join (
select dt.field_value as user_id, dt1.field_value as score
from data_table dt
join data_table dt1 on dt1.submission = dt.submission
join data_table dt2 on dt2.submission = dt1.submission
where dt.field_type='user' and
dt1.field_type = 'score' and
dt2.field_type='completed' and
dt2.field_value = 1
) s
on s.user_id = u.id
工作fiddlehttp://sqlfiddle.com/#!2/d6e72f/19/0
当用户开始向现有结构动态添加附加属性时,我已经看到很多这种情况。您必须先对附加数据进行反透视,然后才能将其视为正常 table。由于 mysql 不支持逆轴,我使用了正常的解决方法。
Select u.name, score
from users u
INNER JOIN (
Select submission,
max(case when field_Type='user' then field_value end) as user,
max(case when field_Type='score' then field_value end) as score,
max(case when field_Type='completed' then field_value end) as completed
FROM data_table
group by submission) dt
on dt.user = u.id
and dt.completed = 1
这假定对于给定的提交,不能有超过一个 field_Type 值组合。如果有,这只会 return 最大值。
基本上,它所做的是将数据反透视到一个 table 结构中,然后我们可以将其连接回去。
我们使用 max 或 min 的原因是为了让给定的提交返回一行而不是多行。同样,这只是简单地取消数据透视并将行组合回一个。但是基于这样一个假设,即在提交中不会重复 field_type 和 field_value。
让我们看看您实际在做什么,以便我们了解问题所在:
Select
u.name,
dt2.field_value as score
from
users u
获取用户列表
left join
data_table dt on u.id=dt.field_value and dt.field_type='user'
仅连接来自 data_table 类型 'user'
的行
left join
data_table dt2 on dt.submission=dt2.submission and dt2.field_type='score'
仅连接 data_table 中 'score' 类型的行。
现在您的结果集类似于:
用户,DT(类型 'USER' 的数据 table 行),DT2(类型 'SCORE' 的数据 table 行)
where
(dt.field_type='completed' and dt.field_value=1)
过滤结果以仅包含 dt.field_type(之前过滤为仅包含类型 'user')具有类型 'complete'.
的用户
基本上,您的联接会过滤掉 'data_table' 中的所有 'complete' 行,因此您的 where 语句找不到匹配项。这只是对正在发生的事情的解释。关于你的问题。
查看您的架构,您有几个选择。尽管我不喜欢这种设计,但我会这样写您的查询:
SELECT U.name, SCORE_DT.field_value AS score
FROM user U
JOIN data_table DT ON DT.field_value=U.id AND DT.field_type="USER"
JOIN data_table SCORE_DT ON SCORE_DT.submission=DT.submission AND SCORE_DT.field_type="SCORE"
JOIN data_table COMPLETED_DT.submission=DT.submission AND COMPLETED_DT.field_type="COMPLETED" AND COMPLETED_DT.field_value=1
实际上,更改 table 设计会让您的生活更轻松,因为此数据结构需要您构建查询,为您感兴趣的每一列执行数据透视操作。对于小型数据集像这样是可行的,但是随着表单中列数的增加,使用起来会变得非常乏味。
另一种有效的变体是...
select x.name
, d.field_value
from data_table d
join (select u.name
, d2.submission
from users u
join data_table d2
on d2.field_value = u.id
and d2.field_type = 'user'
join data_table d3
on d2.submission = d3.submission
and d3.field_type = 'completed'
and d3.field_value = '1'
) x
on x.submission = d.submission
and d.field_type = 'score'
对于您的数据集,您可能会发现此答案或 xQbert 的答案表现不同。
我会给他们两个尝试。根据您的数据,尝试将最内层的查询获取到 return 尽可能小的数据集。例如,如果您知道 data_table 记录中只有一小部分 'completed' = '1',那么第三个嵌套 select 可能不会不合理,如果它导致更小的MySql 的结果。
我正在尝试构建一个查询以从表单生成器中提取数据,在该表单生成器中我只能在另一个字段值等于特定值的情况下加入一个字段值。我的查询 returns 没有行。
users
------------------
|id | name |
------------------
|40 | John |
|45 | Michael |
|47 | Bob |
------------------
data_table
----------------------------------------------------
|id | submission | field_type | field_value |
----------------------------------------------------
|1 | 12345 | user | 40 |
|2 | 12345 | score | 5 |
|3 | 12345 | completed | 1 |
|4 | 23456 | user | 45 |
|5 | 23456 | score | 3 |
|6 | 23456 | completed | 0 |
|7 | 45678 | user | 47 |
|8 | 45678 | score | 2 |
|9 | 45678 | completed | 1 |
----------------------------------------------------
Desired result
---------------
|Name | Score |
---------------
|John | 5 |
|Bob | 2 |
---------------
Select
u.name,
dt2.field_value as score
from
users u
left join
data_table dt on u.id=dt.field_value and dt.field_type='user'
left join
data_table dt2 on dt.submission=dt2.submission and dt2.field_type='score'
where
(dt.field_type='completed' and dt.field_value=1)
你可以做类似的事情(如果你真的不能改变你的数据结构,它看起来......很奇怪)。
您将在 data_table 上有一个子查询,在 table 上有两个自连接(因为您需要 3 个不同的行和条件)
select u.name, s.score
from users u
join (
select dt.field_value as user_id, dt1.field_value as score
from data_table dt
join data_table dt1 on dt1.submission = dt.submission
join data_table dt2 on dt2.submission = dt1.submission
where dt.field_type='user' and
dt1.field_type = 'score' and
dt2.field_type='completed' and
dt2.field_value = 1
) s
on s.user_id = u.id
工作fiddlehttp://sqlfiddle.com/#!2/d6e72f/19/0
当用户开始向现有结构动态添加附加属性时,我已经看到很多这种情况。您必须先对附加数据进行反透视,然后才能将其视为正常 table。由于 mysql 不支持逆轴,我使用了正常的解决方法。
Select u.name, score
from users u
INNER JOIN (
Select submission,
max(case when field_Type='user' then field_value end) as user,
max(case when field_Type='score' then field_value end) as score,
max(case when field_Type='completed' then field_value end) as completed
FROM data_table
group by submission) dt
on dt.user = u.id
and dt.completed = 1
这假定对于给定的提交,不能有超过一个 field_Type 值组合。如果有,这只会 return 最大值。
基本上,它所做的是将数据反透视到一个 table 结构中,然后我们可以将其连接回去。
我们使用 max 或 min 的原因是为了让给定的提交返回一行而不是多行。同样,这只是简单地取消数据透视并将行组合回一个。但是基于这样一个假设,即在提交中不会重复 field_type 和 field_value。
让我们看看您实际在做什么,以便我们了解问题所在:
Select
u.name,
dt2.field_value as score
from
users u
获取用户列表
left join
data_table dt on u.id=dt.field_value and dt.field_type='user'
仅连接来自 data_table 类型 'user'
的行left join
data_table dt2 on dt.submission=dt2.submission and dt2.field_type='score'
仅连接 data_table 中 'score' 类型的行。 现在您的结果集类似于:
用户,DT(类型 'USER' 的数据 table 行),DT2(类型 'SCORE' 的数据 table 行)
where
(dt.field_type='completed' and dt.field_value=1)
过滤结果以仅包含 dt.field_type(之前过滤为仅包含类型 'user')具有类型 'complete'.
的用户基本上,您的联接会过滤掉 'data_table' 中的所有 'complete' 行,因此您的 where 语句找不到匹配项。这只是对正在发生的事情的解释。关于你的问题。
查看您的架构,您有几个选择。尽管我不喜欢这种设计,但我会这样写您的查询:
SELECT U.name, SCORE_DT.field_value AS score
FROM user U
JOIN data_table DT ON DT.field_value=U.id AND DT.field_type="USER"
JOIN data_table SCORE_DT ON SCORE_DT.submission=DT.submission AND SCORE_DT.field_type="SCORE"
JOIN data_table COMPLETED_DT.submission=DT.submission AND COMPLETED_DT.field_type="COMPLETED" AND COMPLETED_DT.field_value=1
实际上,更改 table 设计会让您的生活更轻松,因为此数据结构需要您构建查询,为您感兴趣的每一列执行数据透视操作。对于小型数据集像这样是可行的,但是随着表单中列数的增加,使用起来会变得非常乏味。
另一种有效的变体是...
select x.name
, d.field_value
from data_table d
join (select u.name
, d2.submission
from users u
join data_table d2
on d2.field_value = u.id
and d2.field_type = 'user'
join data_table d3
on d2.submission = d3.submission
and d3.field_type = 'completed'
and d3.field_value = '1'
) x
on x.submission = d.submission
and d.field_type = 'score'
对于您的数据集,您可能会发现此答案或 xQbert 的答案表现不同。 我会给他们两个尝试。根据您的数据,尝试将最内层的查询获取到 return 尽可能小的数据集。例如,如果您知道 data_table 记录中只有一小部分 'completed' = '1',那么第三个嵌套 select 可能不会不合理,如果它导致更小的MySql 的结果。