我在签约工作的应用程序中遇到了一个非常烦人的问题。
数据库结构由于兼容性原因我无法修改是一团糟。它基本上是一个多对多对多对你的头上关系。它是这样的:
与
---------------------------
| contact_id | name | ... |
---------------------------
| 1 | foo | |
---------------------------
metadefinition
-----------------------------
| metadefinition_id | name |
-----------------------------
| 1 | title |
-----------------------------
| 2 | job |
-----------------------------
contact_metadata
----------------------------
| contact_id | metadata_id |
----------------------------
| 1 | 1 |
----------------------------
| 1 | 2 |
----------------------------
元数据
-------------------------------------------
| metadata_id | metadefinition_id | value |
-------------------------------------------
| 1 | 1 | mrs |
-------------------------------------------
| 2 | 2 | coder |
-------------------------------------------
因此,对于单个联系人,我想搜索所有这些并获得类似的内容:
-----------------------------------------------------
| contact_id | name | metadata.title | metadata.job |
-----------------------------------------------------
| 1 | foo | mrs | coder |
-----------------------------------------------------
所以现在到目前为止我已经尝试过了。我可以提前获取metadefinition
列表,这不是真正的问题。所以我构建了一个这样的查询:
SELECT contact.*,m1.value AS `metadata.title` FROM contact
LEFT JOIN contact_metadata ON contact.contact_id = contact_metadata.contact_id
LEFT JOIN metadata m1 ON contact_metadata.metadata_id = m1.metadata_id AND m1.metadefinition_id = 1
GROUP BY contact_id
这适用于单个metadefinition
,我得到这样的内容:
--------------------------------------
| contact_id | name | metadata.title |
--------------------------------------
| 1 | foo | mrs |
--------------------------------------
如果我尝试两个,但是:
SELECT contact.*,m1.value AS `metadata.title`,m2.value AS `metadata.job` FROM contact
LEFT JOIN contact_metadata ON contact.contact_id = contact_metadata.contact_id
LEFT JOIN metadata m1 ON contact_metadata.metadata_id = m1.metadata_id AND m1.metadefinition_id = 1
LEFT JOIN metadata m2 ON contact_metadata.metadata_id = m2.metadata_id AND m2.metadefinition_id = 2
GROUP BY contact_id
我明白了:
-----------------------------------------------------
| contact_id | name | metadata.title | metadata.job |
-----------------------------------------------------
| 1 | foo | mrs | NULL |
-----------------------------------------------------
如果我删除GROUP BY子句,当然,我得到:
-----------------------------------------------------
| contact_id | name | metadata.title | metadata.job |
-----------------------------------------------------
| 1 | foo | mrs | NULL |
-----------------------------------------------------
| 1 | foo | NULL | coder |
-----------------------------------------------------
只要查询时间相对可接受,我就会对任何事情持开放态度(考虑到结构,如果100000记录需要10秒钟,那么它总比没有好)
是不是临时表,存储过程,我不在乎,只要我不用改变实际的数据库结构。相信我,当它成为改造的时候,我很高兴把整件事搞砸。
是否有解决方案,是否可能?
提前致谢。
答案 0 :(得分:6)
我使用了一对相关的子查询。像
这样的东西SELECT c.*
,( Select value from metadata m1
INNER JOIN contact_metadata cm
on cm.metadata_id = m1.metadata_id
WHERE m1.metadefinition_id = 1
AND cm.contact_id = c.contact_id )AS `metadata.title`
,( Select value from metadata m1
INNER JOIN contact_metadata cm
ON cm.metadata_id = m1.metadata_id
WHERE m1.metadefinition_id = 2
AND cm.contact_id = c.contact_id )AS `metadata.job`
FROM contact c
WHERE c.contact_id = 1
答案 1 :(得分:4)
您需要将每个元数据项(标题,作业等)作为单独的子查询获取,然后加入contact_id以将这些元数据项一起提供给每个联系人。像这样:
SELECT contact.*, metadata_title, metadata_job FROM contact
INNER JOIN
(SELECT contact_id,m.value AS `metadata_title` FROM contact_metadata
LEFT JOIN metadata m ON contact_metadata.metadata_id = m.metadata_id AND m.metadefinition_id = 1) metaTitle ON metaTitle.contact_id=contact.contact_id
INNER JOIN
(SELECT contact_id,m.value AS `metadata_job` FROM contact_metadata
LEFT JOIN metadata m ON contact_metadata.metadata_id = m.metadata_id AND m.metadefinition_id = 2) metaJob ON (metaJob.contact_id=contact.contact_id)
如果您想允许客户端没有特定的元数据项,请改用LEFT JOINs
,因为目前,查询只会返回同时定义了作业和标题的客户端。 / p>
答案 2 :(得分:0)
现在我终于正确地读了这个问题,这是我的两分钱,因为这是值得的(没有双关语意)。
SELECT contact.contact_id, contact.name, m1.value AS 'metadata.title', m2.value AS 'metadata.job'
FROM dbo.contact
INNER JOIN dbo.contact_metadata cm1 ON cm1.contact_id = cm1.contact_id
INNER JOIN dbo.metadata m1 ON cm1.metadata_id = m1.metadata_id AND m1.metadefinition_id = 1
INNER JOIN dbo.contact_metadata cm2 ON contact.contact_id = cm2.contact_id
INNER JOIN dbo.metadata m2 ON cm2.metadata_id = m2.metadata_id AND m2.metadefinition_id = 2