【问题标题】: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_idcastle_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 行还是两者都不返回?

标签: php sql postgresql laravel


【解决方案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 小提琴。可以查一下确认吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-17
    • 2021-01-26
    • 1970-01-01
    • 1970-01-01
    • 2011-01-28
    相关资源
    最近更新 更多