多对多关系SELECT问题

时间:2011-04-13 16:03:56

标签: mysql many-to-many

我在签约工作的应用程序中遇到了一个非常烦人的问题。

数据库结构由于兼容性原因我无法修改是一团糟。它基本上是一个多对多对多对你的头上关系。它是这样的:

---------------------------
| 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秒钟,那么它总比没有好)

是不是临时表,存储过程,我不在乎,只要我不用改变实际的数据库结构。

相信我,当它成为改造的时候,我很高兴把整件事搞砸。

是否有解决方案,是否可能?

提前致谢。

3 个答案:

答案 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