2009-06-16 53 views
3

DB结构:更好的SQL查询?

fid 
subid 
fieldname 
fieldval 

要获得创纪录的一个人,我做这样的事情:

$querystr = " 
SELECT FN.sub_id, FN.`First Name` , LN.`Last Name` , DOB.`dob` , EMAIL.`email` , PHONE.`phone` 
FROM 
(SELECT sub_id, field_val AS 'First Name' 
FROM $db->data 
WHERE `field_name` = 'First Name' 
)FN, 
( SELECT sub_id, field_val AS 'Last Name' 
FROM $db->data 
WHERE `field_name` = 'Last Name' 
)LN, 
(SELECT sub_id, field_val AS `Team` 
FROM $db->data 
WHERE `field_name` = 'Team' 
)TEAM, 
(SELECT sub_id, field_val AS `dob` 
FROM $db->data 
WHERE `field_name` = 'DOB' 
)DOB, 
(SELECT sub_id, field_val AS `email` 
FROM $db->data 
WHERE `field_name` = 'EMail' 
)EMAIL, 
(SELECT sub_id, field_val AS `phone` 
FROM $db->data 
WHERE `field_name` = 'Telephone' 
)PHONE 

WHERE FN.sub_id = LN.sub_id 
AND LN.sub_id = DOB.sub_id 
and DOB.sub_id = EMAIL.sub_id 
and EMAIL.sub_id = PHONE.sub_id 
ORDER BY LN.`Last Name` 
"; 

如何简化这有什么建议?

+0

我以前见过这种模式。容易添加“字段”,因为它们从来不存在,但由于需要“伪造”记录,查询速度很慢。你哪一个做更多?查询或添加字段?这应该推动你的优化。 – n8wrl 2009-06-16 15:55:59

回答

6

您可以使表data的这些许多自联接更加明确,这使查询更具可读性,但最有可能不会影响速度。即:

SELECT FN.sub_id, FN.field_val AS `First Name`, 
        LN.field_val AS `Last Name`, 
        DOB.field_val AS `dob`, 
        EMAIL.field_val AS `email`, 
        PHONE.field_val AS `phone` 
FROM $db->data FN 
JOIN $db->data LN ON (LN.field_name = 'Last Name' AND LN.sub_id = FN.sub_id) 
JOIN $db->data TEAM ON (TEAM.field_name = 'Team' AND TEAM.sub_id = FN.sub_id) 
JOIN $db->data DOB ON (DOB.field_name = 'DOB' AND DOB.sub_id = FN.sub_id) 
JOIN $db->data EMAIL ON (EMAIL.field_name = 'EMail' AND EMAIL.sub_id = FN.sub_id) 
JOIN $db->data PHONE ON (PHONE.field_name = 'Telephone' AND PHONE.sub_id = FN.sub_id) 
WHERE FN.field_name = 'First Name' 
ORDER BY LN.field_val 

基本上,很多繁琐的自联接是您支付表的这种“灵活”的组织作为属性名称和值的集合的价格。

顺便说一句,如果某些sub_id的某些数据可能丢失,并且您仍然希望在输出中看到该行(对于丢失的数据为NULL),请使用LEFT JOIN而不是简单的JOIN作为该字段的实例上述查询中的数据。

+0

你可以举一个例子“make ...表格数据更加明确的自连接”吗? – Number8 2009-06-16 15:57:41

+0

在当前编辑中完成。 – 2009-06-16 15:58:07

2

如果可能的话,我会尝试改变你的数据库模式,并创建一个包含所有这些属性(名字,姓氏,团队等)的Person表;对于稍后想保持这一点的人来说,这将更直接。这将使你的查询变得简单。

当然,我不知道你可能有任何额外的要求。我还假设你在使用SQL的关系型数据库上使用这种数据库,而不是其他数据库类型。

1
SELECT FN.sub_id, FN.field_val as `First Name`, LN.field_val as `Last Name`, DOB.field_val as `dob`, EMAIL.field_val as `email`, PHONE.field_val as `phone` 
FROM  $db->data FN 
INNER JOIN $db->data LN on LN.sub_id = FN.sub_id 
INNER JOIN $db->data DOB on DOB.sub_id = FN.sub_id 
INNER JOIN $db->data EMAIL on EMAIL.sub_id = FN.sub_id 
INNER JOIN $db->data PHONE on PHONE.sub_id = FN.sub_id 
WHERE FN.field_name = 'First Name' 
AND LN.field_name = 'Last Name' 
AND DOB.field_name = 'DOB' 
AND EMAIL.field_name = 'EMail' 
AND PHONE.field_name = 'Telephone'; 

(只是展示Alex Martelli的好主意)。

0

就像sheepsimulator,我强烈建议你更新你的数据模型,其中每个字段实际上是它自己的。否则,如果您的RDBMS支持它,使用子选择这样可能会更快,特别是如果你实际上是限制的结果,并在最后一个where子句设置:

select 
    s.field_val as `First Name`, 
    (select field_val from $db->data as i where field_name = 'Last Name' and i.sub_id = s.sub_id) as `Last Name`, 
    (select field_val from $db->data as i where field_name = 'Team' and i.sub_id = s.sub_id) as `Team`, 
    (select field_val from $db->data as i where field_name = 'DOB' and i.sub_id = s.sub_id) as `DOB`, 
    (select field_val from $db->data as i where field_name = 'EMail' and i.sub_id = s.sub_id) as `E-Mail`, 
    (select field_val from $db->data as i where field_name = 'Telephone' and i.sub_id = s.sub_id) as `Phone`, 
from $db->data as s 
order by `Last Name` 
0

选项1:改变你的数据库架构有不属于动态人表。

选项-2:您的代码将只在所有字段都有数据的情况下有效。否则它将不会返回给你。所以不仅对于可读性,而且对于它的工作,您需要使用LEFT JOIN。但我真的会选择1。选项-3:为什么不直接从SQL返回所有代码(从表中选择*),然后使用更适合的编程语言将其转换为对象。

1

使用实际的数据库模式。使用'可以容纳任何东西'的'通用'模式的方法是一个绝对可怕的想法。这种方法多久会一次又一次地被“发现”或“设计”成为“建筑师”,这是惊人的。

这通常被描述为EAV (entity-attribute-value)架构。所有你需要的是四张桌子,然后你的痛苦开始:

* objects 
* attributes 
* object_attributes (objects is 1:M with object_attributes) 
* links (links objects to objects, an association table) 
0

这是这种类型的数据愚蠢的布局。

虽则回答你的问题:

将表与自身在不同的别名。

按不同的字段名称筛选每个别名副本,并假装它们是不同的表。

如果您经常这样做,那么根据此查询创建视图可能会有所帮助,以便将来的查询不必如此冗长。

$querystr = " 
    SELECT 
    fname.sub_id 
    ,fname.field_val as 'First Name' 
    ,lname.field_val as 'Last Name' 
    ,team.field_val as 'Team' 
    ,dob.field_val as 'DOB' 
    ,email.field_val as 'Email' 
    ,phone.field_val as 'Phone' 
    FROM 
    $db->data fname 
    ,$db->data lname 
    ,$db->data team 
    ,$db->data dob 
    ,$db->data email 
    ,$db->data phone 
    where fname.sub_id = lname.sub_id 
    and fname.sub_id = team.sub_id 
    and fname.sub_id = dob.sub_id 
    and fname.sub_id = email.sub_id 
    and fname.sub_id = phone.sub_id 
    and fname.field_name = 'First Name' 
    and lname.field_name = 'Last Name' 
    and team.field_name = 'Team' 
    and dob.field_name = 'DOB' 
    and email.field_name = 'EMail' 
    and phone.field_name = 'Telephone' 
    order by 
    ln.field_val 
";