【问题标题】:PHP dynamic query building to support positive and negative like match queries when nullPHP 动态查询构建以在 null 时支持正负匹配查询
【发布时间】:2016-12-03 16:40:34
【问题描述】:

我有一个采用以下格式的数据库表,其中包含汽车品牌和型号的版本:

汽车品牌、型号表

| car_id | make | model |
-------------------------
| 1      | audi | a1    |
| 2      | audi | a2    |


汽车版本表

| car_version_id | car_id | version   |
---------------------------------------
| 1              | 1      | NULL      |
| 2              | 1      | business  |
| 3              | 2      | NULL      |
| 4              | 2      | business  |
| 5              | 2      | sport     |


查找项表 - 由第三方 api 提供的单独不受控制的数据 - 这是一个样本,更像是数万个

| item_id | make | model | title                                      |
-----------------------------------------------------------------------
| 1       | audi | a1    | audi a1 es edition                         |
| 2       | audi | a1    | audi a1 business edition                   |
| 3       | audi | a2    | audi a2 sport edition 45k miles            |
| 4       | audi | a2    | audi a2 37k miles 2.0 liter                |
| 5       | audi | a1    | audi a1 clean inside, 34k miles, 1.0 liter |

我遍历每个车型,获取每个车型的版本,然后使用数据在汽车列表数据库中搜索 - 两者都是分开的。

目前它将执行以下伪样式查询:

1. SELECT * FROM item WHERE make = audi AND model = 'a1'
2. SELECT * FROM item WHERE make = audi AND model = 'a1' AND title LIKE '%business%'
3. SELECT * FROM item WHERE make = audi AND model = 'a1'
4. SELECT * FROM item WHERE make = audi AND model = 'a1' AND title LIKE '%business%'
5. SELECT * FROM item WHERE make = audi AND model = 'a1' AND title LIKE '%sport%'

问题

这里的问题是商业和运动模型将出现在没有“title LIKE”的搜索查询中,因为值为NULL。

我想要达到的目标如下:

1. SELECT * FROM item WHERE make = audi AND model = 'a1' AND title NOT LIKE '%business%'
2. SELECT * FROM item WHERE make = audi AND model = 'a1' AND title LIKE '%business%'
3. SELECT * FROM item WHERE make = audi AND model = 'a1' AND title NOT LIKE '%business%' AND title NOT LIKE '%sport%'
4. SELECT * FROM item WHERE make = audi AND model = 'a1' AND title LIKE '%business%' AND title NOT LIKE '%sport%'
5. SELECT * FROM item WHERE make = audi AND model = 'a1' AND title LIKE '%sport%' AND title NOT LIKE '%business%'

另一个问题是品牌、型号和版本值的组合是动态的,因此您可能拥有一个品牌、型号和 10 个版本汽车的实例,从而导致查询时间过长。

到目前为止的代码

我在这个项目中使用 php 和 Symfony。

到目前为止,我遍历汽车,获取版本并执行上述查询:

<?php

$cars  = $this->getCars();

foreach ($cars as $car) {

    $make = $car->version;
    $model = $car->model;
    $versions = $car->versions;

    foreach ($versions as $version) {

        $query = "SELECT * FROM item WHERE make = '$make' AND model = '$model'";

        if (!is_null($version)) {
            $query .= " AND title LIKE '%$version%'";
        }

        // do query lookup here
    }
}

问题

所以问题是,我如何扩展当前的 php 解决方案,以将我需要的负匹配与正匹配结合起来?

【问题讨论】:

  • 为什么不使用AND version IS NOT NULL
  • @dekel 问题已更新,这是由于未指定第二个数据库样式和版本 - 它是版本在或不在的数据字符串 - (string|NULL) 所以它需要否定查找。
  • 为什么不使用正确的内连接,以便只获得匹配值而没有空值?顺便说一句,您的表中没有汽车字段...我建议的是 SELECT * FROM car_version_table cvt INNER JOIN ON car_make_model_table cmmt WHERE make = '$make' AND model = '$model' AND ((title LIKE % business%) 或 (title LIKE %sports%))。
  • @helldawg13 不确定正确的连接除了将其放入一个查询之外有什么不同,仍然有同样的问题
  • 如果汽车是奥迪 a1,你能说明预期的输出吗?此外,您似乎想要运行每个单独的选择以一次从item 获取 1 条记录。还是没有?

标签: php mysql database


【解决方案1】:

我认为可以通过一个(诚然,有点粗俗)查询来获得您想要的东西。无需迭代版本,只需通过加入相关表来使用数据库中的版本。

无论您是否要使用 $car->versions 中的版本,答案是使用连接条件中的子查询仅查找不包含任何非空版本的标题。

SELECT ver.version, i.title
FROM car c
INNER JOIN car_version ver ON c.car_id = ver.car_id
INNER JOIN item i ON
    c.make = i.make AND
    c.model = i.model AND (
        ver.version IS NOT NULL AND
        i.title LIKE CONCAT('%', ver.version, '%' OR
        ver.version IS NULL AND
        (
            SELECT COUNT (1)
            FROM car_version ver2
            WHERE ver2.car_id = c.car_id AND
            ver2.version IS NOT NULL AND
            i.title LIKE CONCAT('%', ver2.version, '%'
        ) = 0
    )
WHERE c.make=? AND c.model=?

问号表示使用参数化查询。有一些包含' 的车型会无意中导致sql-injection。只提及它,因为我不知道您是否已经在处理它。

假设您确实想使用$car-&gt;versions。您可以将版本添加到WHERE 子句中。

$query = "... WHERE c.make=? AND c.model=? AND ver.version IN ('" . implode("','", $car->versions) . "';";

这仍然可以让您只运行一个查询来获得所有结果。 (使用 implode 乐观地认为版本中不会有任何 '。您可能必须动态准备查询。)

【讨论】:

    猜你喜欢
    • 2021-08-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-23
    • 1970-01-01
    • 1970-01-01
    • 2019-04-08
    • 2012-12-21
    • 2018-12-21
    相关资源
    最近更新 更多