多列约束查询
Posted
技术标签:
【中文标题】多列约束查询【英文标题】:Constrain query by multiple columns 【发布时间】:2018-06-21 03:41:53 【问题描述】:我的查询:
$units = DB::table('units')
->join('locations', 'locations.id', '=', 'units.location_id')
->join('castles', 'castles.id', '=', 'units.castle_id')
->join('unit_types', 'unit_types.id', '=', 'units.unit_type_id')
->select('units.location_id',
'units.previous_location_id',
'units.id as unit_id',
'units.castle_id',
'castles.guild_id',
'units.unit_type_id',
'units.current_health',
'unit_types.damage',
'unit_types.range')
->get()
->groupBy('location_id', 'castle_id');
产生一些类似这样的测试数据:
"1": [
"location_id": 1,
"previous_location_id": 1,
"unit_id": 2,
"castle_id": 1,
"guild_id": null,
"unit_type_id": 3,
"current_health": 90,
"damage": 10,
"range": 3
],
"2":
"1":
"location_id": 2,
"previous_location_id": 2,
"unit_id": 3,
"castle_id": 2,
"guild_id": null,
"unit_type_id": 5,
"current_health": 100,
"damage": 20,
"range": 2
,
"6":
"location_id": 2,
"previous_location_id": 5,
"unit_id": 7,
"castle_id": 5,
"guild_id": null,
"unit_type_id": 15,
"current_health": 180,
"damage": 20,
"range": 3
,
"3":
"2":
"location_id": 3,
"previous_location_id": 3,
"unit_id": 4,
"castle_id": 3,
"guild_id": null,
"unit_type_id": 1,
"current_health": 100,
"damage": 10,
"range": 1
,
"3":
"location_id": 3,
"previous_location_id": 3,
"unit_id": 5,
"castle_id": 3,
"guild_id": null,
"unit_type_id": 1,
"current_health": 100,
"damage": 10,
"range": 1
,
"4":
"5":
"location_id": 4,
"previous_location_id": 4,
"unit_id": 8,
"castle_id": 4,
"guild_id": null,
"unit_type_id": 20,
"current_health": 300,
"damage": 40,
"range": 2
,
"7":
"location_id": 4,
"previous_location_id": 1,
"unit_id": 1,
"castle_id": 1,
"guild_id": null,
"unit_type_id": 1,
"current_health": 100,
"damage": 10,
"range": 1
,
"5":
"4":
"location_id": 5,
"previous_location_id": 5,
"unit_id": 6,
"castle_id": 5,
"guild_id": null,
"unit_type_id": 15,
"current_health": 180,
"damage": 20,
"range": 3
我正在尝试找到一种方法来限制查询,以便我只得到location_id
相同但castle_id
不同的结果,如下所示:
"2":
"1":
"location_id": 2,
"previous_location_id": 2,
"unit_id": 3,
"castle_id": 2,
"guild_id": null,
"unit_type_id": 5,
"current_health": 100,
"damage": 20,
"range": 2
,
"6":
"location_id": 2,
"previous_location_id": 5,
"unit_id": 7,
"castle_id": 5,
"guild_id": null,
"unit_type_id": 15,
"current_health": 180,
"damage": 20,
"range": 3
,
"4":
"5":
"location_id": 4,
"previous_location_id": 4,
"unit_id": 8,
"castle_id": 4,
"guild_id": null,
"unit_type_id": 20,
"current_health": 300,
"damage": 40,
"range": 2
,
"7":
"location_id": 4,
"previous_location_id": 1,
"unit_id": 1,
"castle_id": 1,
"guild_id": null,
"unit_type_id": 1,
"current_health": 100,
"damage": 10,
"range": 1
,
我尝试了类似 ->whereColumn('units.location_id', '<>', 'units.castle_id')
的方法,但在 castle_id
碰巧有类似密钥的情况下不起作用。
编辑
当我在给定位置有超过 1 个 castle_id
时,这个原始 SQL 会告诉我:
SELECT location_id,
COUNT(DISTINCT castle_id)
FROM units
GROUP BY location_id
ORDER BY COUNT DESC;
但我只想像这样直接选择那些行:
SELECT location_id,
FROM units
WHERE (DISTINCT castle_id);
编辑#2
这是一个假设:
units
有 100 行
10行有location_id: 1
20行有location_id: 3
等等……
在有 location_id: 1
的 10 行中,所有 10 行也有 castle_id: 76
在有location_id: 3
的20行中,有13行有castle_id: 99
,其中7行有castle_id: 42
我正在尝试确定行何时具有相同的 location_id
但 castle_id
不相同。
在这种情况下,我会跳过 10 行,保留符合我的条件的 20 行,然后检查剩余的 70 行。
编辑 #3 这是一个按要求提供的 SQL Fiddle:http://sqlfiddle.com/#!9/fad08
编辑 #4 这是我的新查询:
$units = DB::table('units AS u1')
->select('u1.location_id',
'u1.previous_location_id',
'u1.id as unit_id',
'u1.castle_id',
'c1.guild_id',
'u1.unit_type_id',
'u1.current_health',
'ut.damage',
'ut.range')
->distinct()
->join('units AS u2', 'u1.location_id', '=', 'u2.location_id')
->join('castles AS c1', 'c1.id', '=', 'u1.castle_id')
->join('castles AS c2', 'c2.id', '=', 'u2.castle_id')
->join('unit_types AS ut', 'ut.id', '=', 'u1.unit_type_id')
->whereColumn([
['u1.castle_id', '<>', 'u2.castle_id'],
['c1.guild_id', '<>', 'c2.guild_id']
])
->orderBy('location_id')
->get();
echo $units;
基于 fubar 的回答,在 guild_id
相同时添加了另一个约束。
【问题讨论】:
请详细说明您要达到的目标 @AndréMarcondesTeixeira 我用一个假设编辑了我的原始帖子,希望能阐明我在查询中想要实现的目标。感谢您的帮助! 你能用种子数据创建一个 SQL 小提琴吗?这可能有助于可视化数据,并且肯定有助于测试查询。 @fubar 我在原始帖子中添加了一个 SQL 小提琴。我正在使用 laravel 迁移来建立我的数据库,但我没有太多的 SQL 经验,所以我让它有点简单......你需要我添加更多表或定义主键吗? @deja 真棒。澄清一下,get the results where location_id is the same but castle_id is different
- 根据您刚刚创建的 SQL 小提琴,您希望返回哪些行?对于第 4 行和第 5 行,位置和城堡 id 相同,那么是返回第 4 行、第 5 行还是两者都不返回?
【参考方案1】:
这个查询应该可以解决问题
SELECT id, location_id, castle_id FROM Units WHERE location_id IN (
SELECT a.location_id FROM Units a
JOIN Units b on (
b.location_id = a.location_id AND
b.castle_id <> a.castle_id
)
) ORDER BY location_id
【讨论】:
【参考方案2】:您可以通过以下查询实现您想要的:
SELECT DISTINCT u1.location_id, u1.previous_location_id, u1.id as unit_id, u1.castle_id, c.guild_id, u1.unit_type_id, u1.current_health, ut.damage, ut.srange
FROM units u1
INNER JOIN units u2 ON (u1.location_id = u2.location_id)
INNER JOIN castles c ON (c.id = u1.castle_id)
INNER JOIN unit_types ut ON (ut.id = u1.unit_type_id)
WHERE u1.castle_id <> u2.castle_id
ORDER BY u1.id
还可以查看有效的 SQL Fiddle - http://sqlfiddle.com/#!9/7c9cb8/3
至于使用 Laravel Query Builder 编写这个,它看起来像:
$units = DB::table('units AS u1')
->select('u1.location_id', 'u1.previous_location_id', 'u1.id as unit_id', 'u1.castle_id', 'c.guild_id', 'u1.unit_type_id', 'u1.current_health', 'ut.damage', 'ut.range')
->distinct()
->join('units AS u2', 'u1.location_id', '=', 'u2.location_id')
->join('castles AS c', 'c.id', '=', 'u1.castle_id')
->join('unit_types AS ut', 'ut.id', '=', 'u1.unit_type_id')
->whereColumn('u1.castle_id', '<>', 'u2.castle_id')
->groupBy('u1.id')
->orderBy('u1.id')
->get();
【讨论】:
非常感谢@fubar!这正是我一直在寻找的解决方案。我不确定自己要花多长时间才能解决这个问题,我已经挣扎了好几天了! 每当我在某个位置的单位数量不均时,它就会崩溃。当一个castle_id
有 5 个单位而另一个 castle_id
有 1 个单位时,它会将 1 个单位再复制四次(我认为是通过加入?),以便它们的数量相同。我注意到,因为有多个单元具有相同的主键 id
,这永远不会发生。我可以添加另一个约束以仅显示唯一的user_id
,但我担心它仍会在每个查询中重复这些单元,这将花费大量时间和内存。
你能用重现问题的示例数据更新 SQL 小提琴吗?
我刚刚做了@fubar
看起来使用GROUP BY u1.id
可以解决这个问题。我已经更新了我的答案和 SQL 小提琴。可以查一下确认吗?以上是关于多列约束查询的主要内容,如果未能解决你的问题,请参考以下文章