(道歉,如果这是重复的 - 我已经尝试过搜索,但我可能不知道我正在努力实现的目标 - 请随时纠正我!)
所以我有一个基于PHP的应用程序(Codeigniter,但我在这部分使用普通的SQL语言),它有一个MySQL数据库,有2个表 - '联系人'和'命令'。
为简单起见,我们假设:
在某些时候,我知道,我将不得不为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查询。
所以,我希望我已经正确地解释了它,以便明确用户可以使用此表单来搜索符合许多不同条件的记录 - 理论上,无限数量的条件 - 结束最新的符合此条件的联系人列表。
SELECT * FROM contact
JOIN order ON contact.Id = order.ContactId WHERE (order.ItemBought = 'Adult Membership' AND order.ItemValidDate = '2009/10')
这很好用。
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')
只要用户要求的每个条件都是OR查询,这都可以正常工作。我假设我可以使用括号和OR来构建此查询,因为我喜欢这么大?例如。在2005/06年度,或2006/07年度,或2007/08年度,或2008/09年度等中找到成人会员资格,就像上面的SQL一样,有更多括号加入' OR'?
目前,我一直在尝试UNION,但如果有更多查询要遵循此规定(例如2008年或2009年和2010年的成人会员资格),这意味着要做多个SELECT。 (也许这就是答案?)
e.g。 `SELECT * FROM contact JOIN订单ON contact.Id = order.ContactId WHERE(order.ItemBought =' Adult Membership' AND order.ItemValidDate =' 2009/10')OR(order.ItemBought =' ;成人会员资格' 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)`
我想知道运行这些查询,将结果存储在PHP数组中然后执行IN (*array of ids already selected*)
,但这似乎我没有正确使用SQL。
聪明的人 - 我做错了什么?
非常感谢你提前帮助。
PS。不要求你为我编写代码!
PPS。如果您知道任何好的教程,那么我很乐意跟随它们!
PPPS。如果这是重复,那么请接受我的道歉!
答案 0 :(得分:0)
我认为你必须退后一步,先尝试在纸上形象化你的问题。示例1和2非常简单,但让我们看一下示例3 。
对于所有标准为“AND”或“OR”的情况 - 事情非常简单。做一个很长的时间,只是先说谎。 但是,当你开始混合它们时,你必须回答一个严肃的问题:
如何分割条件 ?
让我们说有人接受了这些标准:
这为您提供了如此多的查询排列!例如:
如果你再添加一个'OR',你将结束数十种组合!让你离开你必须猜测该做什么的地方。甚至不想想如果没有涉及会发生什么......
这不是您问题的直接答案,而是更多指向可能解决方案的指针。上次我们不得不做类似的事情时,我们最终将这些条件组合成块。
您可以在块中添加条件或添加新的搜索块。在上面的示例中将块视为括号。块中的所有内容都是“AND”或“NOT AND”,块之间可以指定“和”或“或”。这样您就可以立即知道如何构建查询。这在独立应用程序中就像一个魅力。在页面上很好地实现它可能有点棘手,但你明白了。
答案 1 :(得分:0)
正如ZorleQ所说它很快就会变得一团糟
对于第3个问题,使用子选择连接的可能解决方案如下
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购买赞助商的记录,并且仅返回未找到匹配项的行(即,检查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
答案 2 :(得分:0)
我对这样的所有问题的解决方案,其中用户可能提供或不提供多个标准如下...(此示例适用于oracle,但也应该能够在MySQL中完成)...
传入所有过滤器变量,无论它们是否为空或填充了值。在这个例子中,我会说我有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函数。如果第一个值不为null,则返回第二个值,否则返回第三个值。如果您没有使用oracle,并且没有NVL2的等效功能,那么只需自己编写该功能即可。
因此,使用上面的示例,代码ALWAYS将所有三个InputVariables传递给select语句,即使它们是NULL。通过使用NVL2或等效函数,如果InputVariable不为null,则仅在InputVariable和Column ONLY之间进行比较;否则它位于Column和Column之间,当然总是为true,从而有效地忽略了你想要的过滤器变量(即空过滤器值匹配所有行 - 即如果用户没有指定LastName,则包括所有LastNames)。
此解决方案允许您使用许多过滤器变量而无需事先进行大量处理 - 每次都将它们全部传递到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。