MySQL使用垂直表连接水平

时间:2017-07-04 16:41:40

标签: mysql sql join

我有以下结构:

CREATE TABLE person {
    id INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(30) NOT NULL,
    email VARCHAR(30) NOT NULL UNIQUE
}

CREATE TABLE field {
    id INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(30) NOT NULL
}

CREATE TABLE fieldPerson {
    id INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    fieldId INT(11) UNSIGNED,
    personId INT(11) UNSIGNED,
    value VARCHAR(30) NOT NULL,
    FOREIGN KEY (fieldId) REFERENCES field(id),
    FOREIGN KEY (personId) REFERENCES person(id)
}

总结如此。基本上我们有默认字段nameemail,我们可以选择创建自定义字段,例如telephoneaddress等。 我们可以有 n 字段。我试图找出一种方法来执行SELECT查询,该查询返回所有字段而不使用子查询或数据库外的额外工作(但是可以使用连接)。

示例(电话是自定义字段):

TABLE person:

id    name       email
1     Test 1     test1@gmail.com

---- 

TABLE field:

id    name
1     telephone
2     address

----

TABLE fieldPerson:

id    fieldId    personId    value
1     1          1           +1 555 555 555
2     2          1           First St.

----   

RESULTING QUERY

personId   name          email            telephone       Address
1          Test 1        test1@gmail.com  +1 555 555 555  First St.

这可能吗?

感谢。

编辑:

如果不可能,您可以建议使用子查询的最佳解决方案,但只能使用SQL。

2 个答案:

答案 0 :(得分:3)

因此,根据您对查询生成的控制程度,如果您可以按照与SELECT PRIMARY KEY相同的顺序生成field列,则可以尝试以下。我想象你知道field中有2个键的内容,以及它们之前的内容(来自之前的查询?)。

SELECT fp.personId,p.name,p.email,fp.value as telephone,fp2.value as Address 
    FROM fieldPerson fp INNER JOIN person p ON p.id = fp.personId 
                        INNER JOIN field f ON f.id = fp.fieldId 
                        INNER JOIN fieldPerson fp2 
    WHERE fp.fieldId = 1 and fp2.fieldId = 2;

考虑到问题中的样本数据,这给了我结果:

personId | name   | email           | telephone      | Address   |
       1 | Test 1 | test1@gmail.com | +1 555 555 555 | First St. |

这取决于您通过先前进行初始查询以了解fields表中定义的任意字段来避免子查询的概念。你没有说明这个表的动态程度,所以这可能不是一个不合理的简化假设。

除此之外,您可以尝试以下方式:

SELECT fp.personId,p.name,p.email,fp.value as telephone,fp2.value as Address 
    FROM fieldPerson fp INNER JOIN person p ON p.id = fp.personId 
                        INNER JOIN field f ON f.id = fp.fieldId 
                        INNER JOIN fieldPerson fp2 
    WHERE fp.fieldId != fp2.fieldId LIMIT 1,1;

这给了我相同的输出而不必知道字段id号,但我确实需要知道字段的名称来生成列名。我同意,LIMIT可以删除其中一个JOIN产品,并且有一种明显不圣洁的感觉。我觉得接近2并没有太大的优势,因为你必须知道列名。

我同意@Mojtaba,但是,如果您不能使用预先查询来了解动态字段名称和ID,那么我相信您将不得不放置子其中的SELECT语句可以在查询时提取这些语句。

答案 1 :(得分:2)

SELECT p.*
     , group_concat(CASE f.name WHEN 'telephone' THEN fp.value END) telephone
     , group_concat(CASE f.name WHEN 'address'   THEN fp.value END) address
  FROM person p
  JOIN field f
    LEFT JOIN fieldPerson fp
      ON    fp.personId = p.id
        AND fp.fieldId = f.id
  GROUP BY p.id