我有一个包含2个引用(user_id
和item_id
)的表,我需要查询以查找具有特定项目的所有用户。
棘手的部分是,我需要根据它们所拥有的结果数量,而不仅仅是基于它们具有的项目来排序结果。
这是表格:
+--------------+-----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-----------------------+------+-----+---------+-------+
| user_id | int(11) | NO | | 0 | |
| item_id | int(11) unsigned | YES | | NULL | |
+--------------+-----------------------+------+-----+---------+-------+
所以我的查询如下:
SELECT user_id, item_id
FROM user_items
WHERE item_id IN (2, 122, 132)
GROUP BY user_id, item_id
HAVING SUM(item_id = 2);
看起来很容易吗?这是困难部分的用武之地:
item_id = 2是必需的 item_id = 122和132是可选的。 132之后的任何东西也是可选的。
我需要根据以下内容订购结果: 1)如果找到所有项目。 2)如果只找到第2和122项。 3)如果只找到第2项。
这是用于摆弄的SQL小提琴文件:http://sqlfiddle.com/#!2/6b1c1/6/0
我在想,如果有一些方法我可以设置,像这样:SELECT查询说
IF (item_id = 2 AND item_id = 122 AND item_id = 132) AS matches_all,
IF (item_id = 2, item_id = 122) AS matches_some,
IF (item_id = 2) AS matches_first
使用更新的查询进行编辑 这是我到目前为止所拥有的。这大约是我需要的95%: http://sqlfiddle.com/#!2/6b1c1/47
SELECT user_id, item_id,
@tmp_1 := IF(SUM(item_id = 2), 1, 0) AS tmp_1,
@tmp_2 := IF(SUM(item_id = 122), 1, 0) AS tmp_2,
@tmp_3 := IF(SUM(item_id = 132), 1, 0) AS tmp_3,
@tmp_4 := IF(SUM(item_id = 126), 1, 0) AS tmp_4,
CAST(@tmp_3 + @tmp_4 AS UNSIGNED) AS total_other
FROM user_items
WHERE item_id IN (2, 122, 132, 126)
GROUP BY user_id
HAVING SUM(item_id = 2)
ORDER BY tmp_1 DESC, tmp_2 DESC, total_other DESC
更多细节:
1)我最多只输入12个项目,因此如果需要,我可以为每个项目分配一个自己的临时区域。
2)上述查询适用于tmp_1和tmp_2。如果我们有一个具有项目2和122的用户,则将它们放在列表的顶部。
对于其余的,3-4(3到12),我需要计算匹配数,这就是我尝试CAST(@tmp_3 + @tmp_4
的原因。我不确定如何计算这些。
3)一旦我完成了项目3 - 12的总计算,那么这将是ORDER BY
子句中的第三个也是最后一个项目。
示例结果
基于SQL小提琴文件中提供的模式,以下是应该使用item_id搜索所有用户返回的结果:2, 122, 132, 126
+---------+--------------+----------------+-------------+
| USER_ID | PRIMARY_ITEM | SECONDARY_ITEM | OTHER_ITEMS |
+---------+--------------+----------------+-------------+
| 39 | 1 | 1 | 2 |
| 54 | 1 | 1 | 0 |
| 55 | 1 | 0 | 0 |
+---------+--------------+----------------+-------------+
答案 0 :(得分:1)
更新:
根据您问题的更新(包括所需的结果集),这是一个返回该结果集的查询。 (这与我在原始答案中解释的内联视图中的查询非常相似)
SELECT i.user_id AS user_id
, MAX(IF(i.item_id= 2 ,1,0)) AS primary_item
, MAX(IF(i.item_id= 122 ,1,0)) AS secondary_item
, MAX(IF(i.item_id= 132 ,1,0)) +
MAX(IF(i.item_id= 126 ,1,0)) AS other_items
FROM user_items i
WHERE i.item_id IN (2, 122, 132, 126)
GROUP BY i.user_id
HAVING primary_item
ORDER
BY primary_item DESC
, secondary_item DESC
, other_items DESC
, i.user_id
请注意,可以扩展计算other_items
列的表达式以处理任意数量的其他items_id值。 (你只是想确保在那里没有指定相同的item_id两次,否则它将被“计数”两次),例如。
, MAX(IF(i.item_id= 132 ,1,0)) +
MAX(IF(i.item_id= 133 ,1,0)) +
MAX(IF(i.item_id= 135 ,1,0)) +
MAX(IF(i.item_id= 137 ,1,0)) +
MAX(IF(i.item_id= 143 ,1,0)) AS other_items
这基本上是对每个项目进行检查,然后导出1或0,然后将1和0加起来得出总数。
另请注意,IF()函数调用不是必需的,这些表达式实际上可以简化为:
, MAX(i.item_id= 2) AS primary_item
, MAX(i.item_id= 122) AS secondary_item
请注意,实际上不需要WHERE
子句来返回正确的结果集。 (但如果它存在,谓词必须匹配SELECT列表中正在检查的item_id值。
另请注意,ORDER BY不需要包含primary_item DESC
,因为我们的查询保证primary_item
的值为1。使用{{1}开始排序就足够了因为那可以是1或0。
覆盖索引secondary_item DESC
可能会加快效果,或者可能是前导列为on (user_id,item_id)
的索引可能更好。 (如果没有WHERE子句,查询将需要检查表中的每一行,基本上是全表扫描或完整索引扫描。)
从结果集中,如果用户有一个或多个项目(而不是他拥有的特定项目的数量,则看起来要返回'1'。)如果要返回什么是每个项目的数量的计数,然后您将item_id
聚合替换为MAX()
聚合,但这对于解密OTHER_ITEMS列的内容更有问题。
请注意,SUM()
子句仅为那些至少拥有HAVING primary_item
之一的用户提供了行。
更新:
弗朗西斯说......那个查询[在你原来的答案中]每个用户都会返回多个结果,这不是我追求的。
答:这是一个很好的示例,其中显示您想要返回的结果集的示例将是有益的。您的查询在SELECT列表中同时包含item_id = 2
和item_id`,并且没有任何迹象表明您希望每个用户只返回一行,或者每个user_id和item_id组合只返回一行。
要实现这一点,只需在user_id
子句之前添加GROUP BY d.user_id
或GROUP BY d.user_id, d.item_id
子句。
<击> 这不优雅,但我认为它会返回您指定的结果集。
ORDER BY
内联视图(查询别名为SELECT d.user_id
, d.item_id
FROM user_items d
JOIN (
SELECT i.user_id
, MAX(IF(i.item_id=2 ,1,0)) AS item_2
, MAX(IF(i.item_id=122,1,0)) AS item_122
, MAX(IF(i.item_id=132,1,0)) AS item_132
FROM user_items i
WHERE i.item_id IN (2, 122, 132)
GROUP BY i.user_id
HAVING item_2
ORDER BY 3 DESC, 4 DESC, 1
) f
ON d.user_id = f.user_id
WHERE d.item_id IN (2, 122, 132)
ORDER BY (f.item_122 AND f.item_132) DESC
, f.item_122 DESC
, d.user_id
, d.item_id
)会“检查”为用户找到哪些项目。
要了解其工作原理,我们首先检查内联视图的结果......
f
这里可以省略 SELECT i.user_id
, MAX(IF(i.item_id=2 ,1,0)) AS item_2
, MAX(IF(i.item_id=122,1,0)) AS item_122
, MAX(IF(i.item_id=132,1,0)) AS item_132
FROM user_items i
WHERE i.item_id IN (2, 122, 132)
GROUP BY i.user_id
HAVING item_2
ORDER BY 3 DESC, 4 DESC, 1
子句。出于我们的目的,我们基本上只是获取user_id的列表,以及他们拥有哪些指定项目的指标。
MAX聚合中的表达式检查item_id是否分别匹配2,122或132,并返回1或0.我们使用WHERE
聚合来提取我们找到的任何值1。
我们确实需要MAX
,因此我们会得到一个不同的user_id列表。
我们使用GROUP BY
子句,以便省略没有HAVING
的用户。它可以写成这样的
item_id = 2
(添加大于零,但这不是必需的,因为我们保证item_2的值为0或1)
这里并不真正需要 HAVING item_2 > 0
(因为我们要将其加入到user_items表中。)(ORDER BY
仅在最外层的查询中需要。)但它确实展示了可以对这个结果集进行排序。
(如果这是我的要求,我可能会停在此处,并使用此结果集;但这不是您指定的结果集。)
我们将该查询(使用它作为内联视图或MySQL术语中的派生表)加入ORDER BY
表,因此我们仅为那些匹配a的用户返回行来自该查询的user_id。
我们需要添加user_items
子句,因此我们只提取指定列表中的WHERE
值。
我们需要item_id
以指定的顺序获取结果集。
<击> 撞击>
答案 1 :(得分:0)
看起来您需要的是需要哪些字段以及哪些字段是可选的规则或映射。如果你有某种数学规则说,我不知道,也许id&lt; 10是必需的,其他一切都是可选的,你可以做一些花哨的条款。
假设item_id是完全随机的,我建议您创建一个对您的项目进行排名/优先级排序的映射表。也许像item_rank表:
------------------------- | item_id | is_optional | ------------------------- | 2 | 1 | ------------------------- | 122 | 0 | ------------------------- | 133 | 0 | -------------------------
然后你的查询是:
SELECT user_map.user_id, user_map.item_id,
FROM user_map
INNER JOIN item_rank
ON user_map.item_id = item_rank.item_id
AND user_map.item_id IN (2,122,133)
GROUP BY user_map.user_id
HAVING item_rank.is_optional > 0
ORDER BY COUNT( user_map.item_id );
我并不完全喜欢这个解决方案,但是如果不知道你在一天结束时想要完成的更多内容,我就无法提供更具体的解决方案。
另一方面,当问题困难时,通常意味着你试图以错误的方式来找他们。当我发现自己处于架构绑定状态时,当我从一开始就重新追踪并思考它时,我总是会找到一个更清晰的解决方案。显然取决于你的距离,但可能是值得的。
祝你好运!答案 2 :(得分:0)
好的,这就是我想出的。我只需要在前2个之后计算任何项目,所以我提出了一个比使用临时字段更清晰的解决方案,并且最终有效。
SELECT user_id,
IF(SUM(item_id = 2), 1, 0) AS primary_item,
IF(SUM(item_id = 122), 1, 0) AS secondary_item,
(IF(SUM(item_id = 132), 1, 0) + IF(SUM(item_id = 126), 1, 0)) AS other_items
FROM user_items
WHERE item_id IN (2, 122, 132, 126)
GROUP BY user_id
HAVING SUM(item_id = 2)
ORDER BY primary_item DESC, secondary_item DESC, other_items DESC
所以这给了我一个第一和第二项的字段,所以我可以看到那些匹配,然后计算所有其余的,最多可以有10个其他项目。
然后根据我们是否有第一项,第二项,然后是所有其他项的总数来订购。
您可以在此处查看最终结果:http://sqlfiddle.com/#!2/6b1c1/131