我被要求进行查询,但这是他们第一次问我这种查询。另外,我不知道如何命名这个问题的标题,所以我想道歉,以防它可能会产生误导。
所以,首先我有两个名为Article和Attribute的表,其关系是多对多的。其中每个都具有以下结构(最重要的列),并带有一些示例:
Article Table Attribute Table
-------------- -----------------------------------------
| Article_id | |Attribute_id| Attribute_Name| Data_type|
| Article1 | | 1 | Article_name | Text |
| Article2 | | 2 | Height | Number |
| Article3 | | 3 | Description | Fulltext|
-------------- -----------------------------------------
如您所见,Article表仅包含文章ID,而其属性的其余部分(如名称或描述)可在Attribute表中找到。 此外,并非所有文章都应具备所有属性。最后但并非最不重要的是,有大量的文章和大量的属性(超过30个),但我只是把几个作为例子。
现在,Article_Attribute表有点混乱,因为它是多年前制作的。它有这个结构,有一些例子:
Article_Attribute table
-------------------------------------------------------------------------
| Article_ID | Attribute_ID | Number_Value | Text_Value | Fulltext_Value |
--------------------------------------------------------------------------
| Article1 | Attribute1 | NULL | Tennis Ball| NULL |
| Article1 | Attribute3 | NULL | NULL | Just a ball |
| Article2 | Attribute1 | NULL | Eraser | NULL |
| Article2 | Attribute3 | NULL | NULL | Paper eraser |
| Article3 | Attribute1 | 13 | NULL | NULL |
| Article3 | Attribute2 | NULL | Pencil | NULL |
| Article3 | Attribute3 | NULL | NULL | A simple pencil|
--------------------------------------------------------------------------
因此它为每个属性的data_type包含三列而不是一个单值列。
现在我需要一个查询来获得这样的结果:
--------------------------------------------------------------------------
| Article_ID | Article_Name |Article_Height| Article_Description |
--------------------------------------------------------------------------
| Article1 | Tennis Ball | NULL | Just a ball |
| Article2 | Eraser | NULL | Paper eraser |
| Article3 | Pencil | 13 | A simple pencil |
--------------------------------------------------------------------------
到目前为止,我有这么简单的查询:
select a.article_id, attr.attribute_name, ae.number_value, ae.text_value, ae.fulltext_value
FROM article as a
LEFT JOIN article_attribute as ae ON a.article_id = ae.article_id
LEFT JOIN attribute as attr ON ae.attribute_id = attr.id
ORDER BY a.article_id ASC
这给了我一些东西..
----------------------------------------------------------------------------
| Article_ID | Attribute_name | Number_Value | Text_Value | Fulltext_Value |
----------------------------------------------------------------------------
| Article1 | Article_name | NULL | Tennis Ball| NULL |
| Article1 | Description | NULL | NULL | Just a ball |
| Article2 | Article_name | NULL | Eraser | NULL |
| Article2 | Description | NULL | NULL | Paper eraser |
| Article3 | Article_name | 13 | NULL | NULL |
| Article3 | Height | NULL | Pencil | NULL |
| Article3 | Description | NULL | NULL | A simple pencil|
----------------------------------------------------------------------------
这基本上是我可以去的,因为我对查询不好......
我该怎么办?我应该使用存储过程吗?如何将属性名称作为列?
非常感谢任何帮助,我想提前感谢你。
修改: 我没有注意到属性表上的一个大细节:有一些属性实际上有其他表作为Data_type(哦天啊..)。例如,有颜色属性:
Attribute Table
------------------------------------------
|Attribute_id| Attribute_Name| Data_type |
| 1 | Article_name | Text |
| 2 | Height | Number |
| 3 | Description | Fulltext |
| 4 | Color |Color_Table|
------------------------------------------
在这张桌子上,我们只找到color_id及其名称,如:
Color Table
------------------------------
| ID | Name |
| 1 | White |
| 2 | Black |
------------------------------
在article_attribute表中,您可以在Number_value列中找到关系的值。
还可以吗?有6个属性实际链接到其他表。如果这是不可能的,因为这个数据库真的搞砸了很多年前已经搞好了,那么没关系,我仍然会欣赏并接受你的回答。
答案 0 :(得分:2)
您尝试执行的操作称为" pivot"。
虽然其他一些RDBMS本身提供了有效的语法来实现这样的操作,但MySQL却没有 - 它的开发人员认为这种数据操作纯粹是表示性的,因此更适合于更高层的应用程序代码,而不是与之相关的东西。数据库应该负担。
如果绝对必须在MySQL中执行数据透视,那么以真正动态的方式完成它的唯一方法(即不使用属性或其数据类型的任何预知)是通过使用&# 34;动态SQL" -ie从最初操作的结果动态组装最终的DML语句。我们希望执行的最终声明如下所示:
SELECT aa.Article_ID,
GROUP_CONCAT(IF(
a.Attribute_Name = 'Name',
aa.Text_Value,
NULL
)) AS Article_Name,
GROUP_CONCAT(IF(
a.Attribute_Name = 'Height',
aa.Number_Value,
NULL
)) AS Article_Height,
GROUP_CONCAT(IF(
a.Attribute_Name = 'Description',
aa.Fulltext_Value,
NULL
)) AS Article_Description
FROM Article_Attribute aa JOIN Attribute a USING (Attribute_ID)
GROUP BY aa.Article_ID
在sqlfiddle上查看。
你能看到发生了什么吗?我们按文章分组 Article_Attribute
表,然后使用GROUP_CONCAT()
将IF()
操作的结果合并为NULL
但是想要的记录。
我们可以使用我们选择的任何语言从Attribute
表的内容动态生成这样的语句,并且您可能会发现在应用程序代码中这样做最容易(并且最容易理解)
恰好SQL本身也提供了the facility for generating and utilising dynamic statements,并且由于您没有说明您的应用程序是用什么语言开发的,我在下面展示了如何做到这一点:
SET group_concat_max_len = 4294967295; -- to overcome default 1KB limitation
SELECT CONCAT('
SELECT aa.Article_ID, ', GROUP_CONCAT('
GROUP_CONCAT(IF(
a.Attribute_Name = ', QUOTE(Attribute_Name), ',
aa.`', REPLACE(Data_type, '`', '``'), '_Value`,
NULL
)) AS `Article_', REPLACE(Attribute_Name, '`', '``'), '`
' SEPARATOR ','), '
FROM Article_Attribute aa JOIN Attribute a USING (Attribute_ID)
GROUP BY aa.Article_ID
') INTO @sql FROM Attribute;
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
在sqlfiddle上查看。
希望您能看到最终语句是如何从Attribute
表格构建的(它是字符串,以红色突出显示),并且能够移植到您喜欢的任何语言。我引用并使用REPLACE()
来转义恰好出现在属性或数据类型名称中的任何文字引号字符。
答案 1 :(得分:0)
我发现以下内容根据您提供的数据提供了所需的结果:
SELECT a1.ArticleID,MAX(Text_Value) AS Article_Name, MAX(Number_Value) AS Article_Height, MAX(Fulltext_Value) AS Article_Description
FROM Article_Attribute a1
GROUP BY ArticleID
;
我有点怀疑在这里使用MAX,因为它只能工作,因为NULL始终低于非null。我更喜欢像COALESCE这样的东西,但这不是一个聚合函数,因此给GROUP BY带来了不可靠的结果。