SQL查询将乐高设计与件数和件数相匹配

时间:2012-06-11 09:19:29

标签: mysql sql

我正在尝试找到最快/更简单的方法来获得以下问题的结果。

我有一个(例如)Lego Kits的数据库,其中每个Kit都有一个描述和一个需要的乐高积木清单以及它们中的多少。用户可以插入他的Lego乐器系列,然后询问他可以用他的乐曲制作什么Kit以及如果他购买其他乐曲他可以建造什么(可能是第一个限制是他只能购买1种乐曲)。

我所拥有的大致是:

LegoDesign
- id
- name

LegoBlock
- id
- type
- weight
- description

LegoBlockForDesign
- LegoDesign.id
- LegoBlock.id
- numberOfPiecesNeeded

Collection
(- User.id)
- LegoBlock.id
- quantityAvailable

例如,数据库包含StarWar死星的LegoDesign。 LegoBlock包含很长的列表,如“2x2黑色方块”或“小轮”等.LegoBlockForDesign将LegoBlocks分配给死星的LegoDesign(例如1000件“2x2黑色方块”)。 Table集合包含用户拥有的部分。 现在的问题是我必须使用用户拥有的部分查询设计,这意味着首先检查LegoBlock.id,然后检查numberOfPiecesNeeded,因为我可以有一些2x2黑色方块而不足以构建死亡星。这是第一个查询。第二个应该检查包含我有的块的设计加上我的集合中没有的一些块。这意味着我应该检查我所拥有的LegoBlock,但是数量少于适当的数量,并且设计中包含不在我手中的块。后者需要一个可以手动设置的限制。我想让用户在购买件数限制(即最多30件)或者件数难度限制之间进行选择(即没有“特殊区块”可以购买,比如只有一些特殊的轮子或角色存在具体设计(例如星球大战的人物))。

我不完全确定它可以在SQL中完成,特别是因为我必须检查数量而不仅仅是存在块。

编辑: 我添加了LegoBlock.type和LegoBlock.weight。通过这种方式,我可以定义type = common,rare,unique来定义普通片段或特定片段(比如可以定义为罕见的星球大战角色。我不想购买这些片段,因为它们只能用于星球大战'设计)。重量可以用来给予优先权(我喜欢蓝色,所以我更愿意看到设计,我必须购买蓝色碎片)。

3 个答案:

答案 0 :(得分:3)

尝试此查询...这将为您提供LegoDesigns,其中用户拥有以下所有块和数量的块:

SELECT
    c.id, c.name
FROM
    Collection a
INNER JOIN
    LegoBlockForDesign b ON 
        a.LegoBlock.id = b.LegoBlock.id AND
        a.quantityAvailable >= b.numberOfPiecesNeeded
INNER JOIN
    LegoDesign c ON b.LegoDesign.id = c.id
INNER JOIN
    (
        SELECT LegoDesign.id, COUNT(1) AS totalneeded
        FROM LegoBlockForDesign
        GROUP BY LegoDesign.id
    ) d ON c.id = d.LegoDesign.id
WHERE
    a.User.id = <userid here>
GROUP BY
    c.id, c.name, d.totalneeded
HAVING 
    COUNT(1) = d.totalneeded

编辑2:此查询将检索用户可以根据当前集合构建的所有设计以及用户选择购买的其他乐高模块:

SELECT 
    a.id, a.name
FROM
    (
        SELECT
            c.id, c.name, NULL AS notInCollection
        FROM
            Collection a
        INNER JOIN
            LegoBlockForDesign b ON a.LegoBlock.id = b.LegoBlock.id
        INNER JOIN
            LegoDesign c ON b.LegoDesign.id = c.id
        WHERE
            a.quantityAvailable >= b.numberOfPiecesNeeded AND
            a.User.id = <userid here>
        UNION ALL
        SELECT
            d.id, d.name, 1 AS notInCollection
        FROM
            LegoDesign d
        INNER JOIN
            LegoBlockForDesign e ON d.id = e.LegoDesign.id
        WHERE
            e.LegoBlock.id IN (<comma sepd list of legoblockids here>)
    ) a
INNER JOIN
    (
        SELECT LegoDesign.id, COUNT(1) AS totalneeded
        FROM LegoBlockForDesign
        GROUP BY LegoDesign.id
    ) b ON a.id = b.LegoDesign.id
GROUP BY 
    a.id, a.name, b.totalneeded
HAVING 
    COUNT(1) = b.totalneeded AND 
    COUNT(a.notInCollection) > 0

UNION ALL基本上表示包含那些特定选定块的设计的行,以便外部的HAVING COUNT(*)可以将它们考虑在内。 FROM子选择中的notInCollection字段是一个标志,指示该部件是否已经在用户的集合中......所以HAVING COUNT(f.notInCollection) > 0排除了用户已拥有其所有部件的设计采集。这比执行NOT IN (<1st query as subquery>)要高效得多。

该查询假设用户先前已经出现,并且只能从他/她尚未拥有的legoblock列表中进行选择,否则UNION中将会出现重复,这会导致结果丢失。

您还可以输入一个legoblockids列表,以便用户可以选择多个legoblock来查看他们在选择更多块时可以构建的越来越多的设计。

答案 1 :(得分:2)

首次查询。 这将为您提供块名称,以及所需部件之间的差异以及用户具有BY LegoBlock类型的部分(如果用户具有足够或超过需要的值,则为0)。

如果您不想要所有细节,可以将其用作子查询

select lbd.legoBlock_id, lb.Description, Greatest(lbd.NumberOfPiecesNeeded - Coalesce(c.quantityAvailable, 0), 0)
from legoBlockForDesign lbd
inner join LegoBlock lb on lb.Id = lbd.LegoBlock_id

left join Collection c on c.legoBlock_id = lbd.legoBlock_id
where lbd.LegoDesign_id = <the design id queried>
and c.User_Id = <the user id queried>
-- if you want only the blocks with missing quantity
--and Greatest(lbd.NumberOfPiecesNeeded - Coalesce(c.quantityAvailable, 0), 0) > 0

第二个查询很难回答,因为我不知道你想使用哪个过滤器(“特殊块”没有出现在模型中,我认为它在LegoBlock表中,只是确认)。 什么意思是“失踪的数量”?它是全局的(30 = 15 2 * 2 + 10 3 * 1 + 5 125 * 3)?

好吧,使用数量过滤器,你可以做类似的事情

第二次查询

select ld.Id, ld.Description, Sum(Greatest(lbd.NumberOfPiecesNeeded - Coalesce(c.quantityAvailable, 0), 0)) missingPieces
from legoDesign ld
inner join LegoBlockDesign lbd on lbd.LegoDesign_Id = ld.Id
inner join LegoBlock lb on lb.Id = lbd.LegoBlock_id
left join Collection c on c.legoBlock_id = lbd.legoBlock_id
where c.User_Id = <the user id queried>
group by ld.Id, ld.Description
--Filter on quantity
-- having Sum(Greatest(lbd.NumberOfPiecesNeeded - Coalesce(c.quantityAvailable, 0), 0))<=30
--ORDER BY the "less missing pieces"
--ORDER BY Sum(Greatest(lbd.NumberOfPiecesNeeded - Coalesce(c.quantityAvailable, 0)

答案 2 :(得分:0)

第一个查询 - 通过查看设计,在该设计之后剩下0个或更多个块用于该设计中使用的块,我们可以获得用户可以从他的集合构建的设计。

  select design 
  from    (select lb.*, ld.id ,lbfd.numberOfPiecesNeeded numberOfPiecesNeeded,
                  ld.name design 
           from LegoDesign ld, LegoBlock lb ,LegoBlockForDesign lbfd 
           where ld.id=lbfd.id 
           and lb.id=lbfd.lbid ) a 
  left outer join Collection coll
        on coll.legoblock_id = a.legoblock_id 
        and coll.user_id=`<USER_ID> `
  group by a.legodesign_id 
  having min(coalesce(coll.quantityAvailable,0)-a.numberOfPiecesNeeded)>=0;

对于第二个查询,更改像此having这样的sum(a.numberOfPiecesNeeded-coalesce(coll.quantityAvailable,0)) between 1 and x子句将为设计提供完成设计所需的最多x个部分。我们可以在同一查询中使用LegoBlockForDesign和Legoblock加入这些数据,以获取每个单独的块数