通过多列约束查询

时间:2018-01-11 18:06:06

标签: php sql postgresql laravel

我的查询:

    $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碰巧有类似密钥的情况下无效。

修改的 这个原始SQL告诉我,当我在给定位置上有多个castle_id时:

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 13行的{20}行中有castle_id: 99,其中7行有castle_id: 42

我正在尝试确定哪些行具有相同的location_id,但castle_id不相同。

在这种情况下,我会跳过10行,保留符合我标准的20行,然后检查剩余的70行。

编辑#3 这是一个要求的SQL小提琴: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相同时添加了另一个约束。

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

您可以使用以下查询实现所需目的:

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小提琴 - 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();