我有两个表,如下所示
工具:
id | part name
---------------
0 | hammer
1 | sickle
2 | axe
人:
personID | ownedTool1 | ownedTool2 | ownedTool3 ..... ownedTool20
------------------------------------------------------------------
0 | 2 | 1 | 3 ... ... 0
我试图找出有多少人拥有某个特定工具。一个人不能拥有同一工具的多个副本。
我能想到的唯一方法就是
SELECT COUNT(*)
FROM tools JOIN people ON tools.id = people.ownedTool1.id OR tools.id = people.ownedTool2 ... and so on
WHERE tools.id = 0
获得拥有锤子的人数。我相信这会有效,但是,这涉及在查询中包含20个OR
语句。当然有一种更合适的方式来形成这样的查询,我有兴趣学习如何做到这一点。
答案 0 :(得分:2)
首先,您不应该有20列可能包含ID。您应该正确建立规范化架构。如果工具只能属于一个用户 - 但用户可以拥有多个工具,则应建立一对多关系。每个工具的行中都有一个用户ID,映射回它所属的用户。如果某个工具可以属于一个或多个用户,则需要建立多对多关系。这将需要一个包含user_id到tool_id映射行的中间表。如此正确地设置架构将使您想要执行的查询变得微不足道。
在您的特定情况下,似乎用户可以拥有许多工具,并且许多用户可以“共享”工具。对于您的多对多关系,您所要做的就是计算具有所需tool_id的中间表中的行数。
这样的事情:
SELECT COUNT(ID) FROM UserTools Where ToolID = @desired_tool_id
谷歌搜索我加粗的术语应该让你指向正确的方向。如果您坚持使用该架构,那么您指出的方式是唯一的方法。
答案 1 :(得分:2)
如果您无法更改模型(我相信您会告诉我们),那么解决这个损坏的数据模型的唯一合理方法是创建一个视图,它将为您提供标准化视图(双关语)数据:
create view normalized_people
as
select personid,
ownedTool1 as toolid
from people
union all
select personid,
ownedTool2 as toolid
from people
select personid,
ownedTool3 as toolid
from people
... you get the picture ...
然后您的查询就像
一样简单select count(personid)
from normalized_people
where toolid = 0;
答案 2 :(得分:2)
您收到了关于数据库设计的(有保证的)讲座 至于你的问题,有一个简单的方法:
SELECT count(*) AS person_ct
FROM tbl t
WHERE translate((t)::text, '()', ',,')
~~ ('%,' || @desired_tool_id::text || ',%')
或者,如果第一列是person_id
,并且您想要从搜索中排除该列:
SELECT count(*) AS person_ct
FROM tbl t
WHERE replace((t)::text, ')', ',')
~~ ('%,' || @desired_tool_id::text || ',%')
每个表都伴随着PostgreSQL中匹配的复合类型。因此,您可以通过以下方式查询任何表:
SELECT (tbl) FROM tbl;
每行产生一列,占据整行。
PostgreSQL可以一举将这样的行类型转换为文本:(tbl)::text
我用逗号()
替换了两个parens ,
,因此该行的每个值都以逗号,
分隔。
我的第二个查询未翻译左括号,因此第一列(person_id
)将从搜索中排除。
现在,我可以使用逗号~~
~~ %,17,%
)表达式搜索所有列
Voilá:所有这些都是通过一个简单的命令完成的。这是可靠的,只要您的表格中没有text
或int[]
这样的列,这些列也可以在其值中包含,17,
,或者包含数字的其他列,这可能会导致误报。
它无法提供性能奇迹,因为它无法使用标准索引。 (你可以使用pg 9.1中的tgrm模块在表达式上创建GiST或GIN索引,但这是另一个故事。)
无论如何,如果你想进行优化,最好先按照建议的规范化你的表格布局。