3

(抱歉,如果这是重复的 - 我已经尝试过搜索,但我可能不知道我想要达到的目标的正确词 - 请随时纠正我!)

的背景

所以我有一个基于 PHP 的应用程序(Codeigniter,但我在这部分使用普通的 SQL 语言),它有一个 MySQL 数据库,有 2 个表 - 'contact' 和 'order'。

为简单起见,我们假设:

  • 'contact' 有 3 个列:Id、FirstName、LastName
  • “订单”有 4 个列:Id、ContactId、ItemBought、ItemValidDate
  • “订单”表中的一行示例:22、11、成人会员资格、2012/13
  • Id 是两个表的主键,ContactId 是“联系人”的外键,ItemBought 和 ItemValidDate 都是简单的 varchar(我们存储的是“季节”而不是日期——我知道,它并不理想,但它是客户想要的)

在某些时候,我知道,我将不得不将其扩展为 3 个表并使用 OrderItem 表,以允许一个订单有多个项目,所以我想找到一个可以构建的解决方案。但目前,我什至不了解基础知识,所以我将其保留在 2 张桌子上

问题

我想创建一个搜索页面,允许用户根据许多不同的条件查找记录子集。

查看搜索页面的屏幕截图

此表单作为一组标准提交,如下所示:

[order_type_operator] => Array
    (
        [0] => equal
        [1] => equalor
        [2] => notequal
    )

[order_type] => Array
    (
        [0] => Adult Membership
        [1] => Adult Membership
        [2] => Adult Membership
    )

[order_expire] => Array
    (
        [0] => 2005/06
        [1] => 2006/07
        [2] => 2010/11
    )

[submit] => Start Search

然后我循环遍历这个数组,测试是否已经提交了值,并构建了我的 SQL 查询。

所以,我希望我已经正确解释了它,以便清楚用户可以使用此表单来搜索匹配许多不同条件的记录 - 理论上,无限数量的条件 -最终得到一个匹配的联系人列表这个标准

我试过的

示例 1 - 简单的 WHERE

  • "查找在 '2009/10' 中具有 'Adult Membership' 订单记录的联系人记录"
  • IESELECT * FROM contact JOIN order ON contact.Id = order.ContactId WHERE (order.ItemBought = 'Adult Membership' AND order.ItemValidDate = '2009/10')

这工作正常。

示例 2 - WHERE 或 WHERE

  • “查找在 '2009/10' 中具有 'Adult Membership' 订单记录的联系人记录”在 '2010/10' 中具有 'Adult Membership' 订单记录
  • IESELECT * FROM contact JOIN order ON contact.Id = order.ContactId WHERE (order.ItemBought = 'Adult Membership' AND order.ItemValidDate = '2009/10') OR (order.ItemBought = 'Adult Membership' AND order.ItemValidDate = '2010/11')

只要用户要求的每个条件都是 OR 查询,这就可以正常工作。我假设我可以使用方括号和 OR 来构建这个查询,只要我喜欢就可以?例如,在 2005/06、OR 2006/07、OR 2007/08、OR 2008/09 等中查找成人会员资格会像上面的 SQL 一样,用“OR”连接更多的括号?

示例 3 - WHERE 和 WHERE - 我被卡住了!

  • “查找在 '2009/10' 或 2010/11 年具有 'Adult Membership' 订单记录并且在 '2012/13' 中具有'Adult Membership' 订单记录的联系人记录

目前,我一直在尝试 UNION,但是如果有更多查询要遵循这个(例如 2008 年或 2009 年和 2010 年的成人会员资格),这意味着要执行多个 SELECT。(也许这就是答案?)

例如`SELECT * FROM contact JOIN order ON contact.Id = order.ContactId WHERE (order.ItemBought = 'Adult Membership' AND order.ItemValidDate = '2009/10') OR (order.ItemBought = 'Adult Membership' AND order. ItemValidDate = '2010/11')

UNION

SELECT * FROM contact
JOIN order ON contact.Id = order.ContactId WHERE (order.ItemBought = 'Adult Membership' AND order.ItemValidDate = '2012/13)`

示例 4 - 但没有记录....让我大吃一惊

  • “查找在 '2009/10' 中有 'Adult Membership' 订单记录并且在 '2010/10' 中有 'Adult Membership' 订单记录但在 2007/08 年没有'Sponsorship' 订单的联系人记录

我想知道运行这些查询,将结果存储在 PHP 数组中,然后执行 a IN (*array of ids already selected*),但这似乎我没有正确使用 SQL。

这么聪明的人——我做错了什么?

非常感谢您的帮助。

PS。不是要你为我写代码!

聚苯乙烯。如果您知道任何好的教程,那么我会很高兴地关注它们!

购买力平价。如果这是重复的,那么请接受我的道歉!

4

3 回答 3

0

我认为你必须退后一步,先试着在纸上想象你的问题。示例 1 和 2 非常简单,但让我们看一下示例 3

对于所有条件都是“AND”或“OR”的情况 - 事情非常简单。只是做一个很长的地方,只是之前说谎。但是,当您开始混合它们时,您必须回答自己一个严肃的问题:

你如何拆分条件

假设有人选择了这些标准:

  1. 和一个
  2. 或乙
  3. 和 C

这为您的查询提供了如此多的排列!例如:

  1. (A 或 B)和 C
  2. A 或(B 和 C)
  3. (A 和 C)或 B

如果你再添加一个“或”,你会用几十个组合来结束它!把你留在一个你必须猜测该做什么的地方。甚至不想去想如果没有参与会发生什么...

这不是对您问题的直接回答,而是更多指向可能解决方案的指针。上次我们不得不做类似的事情时,我们最终将条件组合成块。

您可以在块中添加条件或添加新的搜索块。将块视为上面示例中的括号。块中的所有内容都是“与”或“非与”,块之间可以指定“与”或“或”。这样您就可以立即知道如何构建您的查询。这在独立应用程序中就像一个魅力。在页面上很好地实现它可能有点棘手,但你明白了。

于 2013-06-14T08:50:41.437 回答
0

对于用户可能提供或不提供多个标准的所有问题,我的解决方案如下......(这个例子适用于oracle,但也应该能够在MySQL中完成)......

您传入所有过滤器变量,无论它们是 null 还是填充了一个值。在这个例子中,我会说我有 3 个值,用户可能会或可能不会填写它们作为 SELECT 上的过滤器。

SELECT
   *
FROM
    table
WHERE
    NVL2(InputVariable1, InputVariable1, Column1) = Column1
OR  NVL2(InputVariable2, InputVariable2, Column2) = Column2 
OR  NVL2(InputVariable3, InputVariable3, Column3) = Column3

NVL2 - 这是一个预言机函数。如果第一个值不为空,则返回第二个值,否则返回第三个值。如果你没有使用 oracle,并且没有 NVL2 的等效函数,只需自己编写函数即可。

因此,使用上面的示例,代码总是将所有三个 InputVariables 传递到 select 语句中,即使它们为 NULL。通过使用 NVL2 或等效函数,仅当 InputVariable 不为空时才在 InputVariable 和 Column 之间进行比较;否则它在 Column 和 Column 之间,这当然总是正确的,从而有效地忽略了您想要的过滤器变量(即空过滤器值匹配所有行 - 即如果用户未指定 LastName,则包括所有姓氏)。

此解决方案允许您使用许多过滤器变量,而无需预先进行大量处理 - 每次只需将它们全部传递到 SELECT 中,无论它们是否为空。

如果您有一组过滤器变量(即用户通过复选框或某种类似机制启用一组输入值),您可以在 CASE 语句中执行上述操作。每种情况都应检查给定集合的启用值,并返回评估整个过滤器变量集的结果(与上面完全相同)。然后将整个 CASE 结构的结果与 1 进行比较,如...

WHERE

CASE [ expression ]

    WHEN enableSet1 
    THEN     NVL2(InputVariable1, InputVariable1, Column1) = Column1
         OR  NVL2(InputVariable2, InputVariable2, Column2) = Column2 
         OR  NVL2(InputVariable3, InputVariable3, Column3) = Column3

   WHEN condition_2 THEN result_2
   ...
   WHEN condition_n THEN result_n

END = 1

这是因为 CASE 结构的值是 THEN 块被评估的结果。

这将允许您在单个 SELECT 语句的范围内执行所有或大部分所需的过滤 - 同样,无需进行大量预处理来构建 SELECT。

于 2014-01-24T19:17:00.157 回答
0

正如 ZorleQ 所说,它很快就会变得一团糟

对于您的第三个问题,使用子选择连接的可能解决方案如下

SELECT contact.*, order.*
FROM contact
INNER JOIN order
ON contact.Id = order.ContactId
INNER JOIN (SELECT DISTINCT ContactId 
            FROM order 
            WHERE (ItemBought = 'Adult Membership' AND ItemValidDate = '2009/10') 
            OR (ItemBought = 'Adult Membership' AND ItemValidDate = '2010/11')) Sub1
ON contact.Id = Sub1.ContactId
INNER JOIN (SELECT DISTINCT ContactId
            FROM order 
            WHERE (ItemBought = 'Adult Membership' AND ItemValidDate = '2012/13')) Sub2
ON contact.Id = Sub2.ContactId

您可以在不使用子选择的情况下执行此操作,而只需一个简单的连接,如下所示

SELECT contact.*, order.*
FROM contact
INNER JOIN order
ON contact.Id = order.ContactId
LEFT OUTER JOIN order Sub1
ON contact.Id = Sub1.ContactId AND Sub1.ItemBought = 'Adult Membership' AND Sub1.ItemValidDate = '2009/10'
LEFT OUTER JOIN order Sub2
ON contact.Id = Sub2.ContactId AND Sub2.ItemBought = 'Adult Membership' AND Sub2.ItemValidDate = '2010/11'
INNER JOIN order  Sub3
ON contact.Id = Sub3.ContactId AND Sub3.ItemBought = 'Adult Membership' AND Sub3.ItemValidDate = '2012/13'
WHERE Sub1.ContactId IS NOT NULL OR Sub2 IS NOT NULL

您的第 4 个问题可以使用 LEFT OUTER JOIN 来查找 2007/08 年购买的 Sponsorship 记录,并且只返回未找到匹配项的行(即检查 LEFT OUTER JOINed 表上的 ContactId 是否为 NULL)。

SELECT contact.*, order.*
FROM contact
INNER JOIN order
ON contact.Id = order.ContactId
INNER JOIN order Sub1
ON contact.Id = Sub1.ContactId AND Sub1.ItemBought = 'Adult Membership' AND Sub1.ItemValidDate = '2009/10'
INNER JOIN order Sub2
ON contact.Id = Sub2.ContactId AND Sub2.ItemBought = 'Adult Membership' AND Sub2.ItemValidDate = '2010/10'
LEFT OUTER JOIN order Sub3
ON contact.Id = Sub3.ContactId AND Sub3.ItemBought = 'Sponsorship' AND Sub3.ItemValidDate = '2007/08'
WHERE Sub3.ContactId IS NULL
于 2013-06-14T09:04:55.500 回答