简体   繁体   English

MySQL EAV SELECT一对多单行结果

[英]MySQL EAV SELECT one-to-many single row results

I've seen similar questions on here, but haven't been able to find one that matches my specific scenario. 我在这里看过类似的问题,但是找不到符合我特定情况的问题。 I have the following three tables: 我有以下三个表:

content
id, [other fields]

attributes
id, title

attributes_values
id, attribute_id[foreign to attributes.id], content_id[foreign to content.id], value

What I'd like is a single query (or group of sub-queries) that can return the appropriate data to avoid having to manipulate this in the programming. 我想要的是一个单一查询(或一组子查询),它可以返回适当的数据,以避免在编程中操纵它。

A little more info about the tables: attributes has three records (Model #, Part #, and Show in Cart). 关于表的更多信息:属性有三个记录(Model#,Part#和Show in Cart)。

I need to select all records from content where the attribute_value for Show in Cart = 1. Along with this, I'd like the other related attribute_values to be returned as their related attribute.title. 我需要选择来自内容的所有记录,其中show in Cart = 1的attribute_value。除此之外,我还希望将其他相关的attribute_values作为其相关的attribute.title返回。 The results would look something like this: 结果看起来像这样:

id    [other fields]    Model # [from attributes.title field]    Part # [from attributes.title field]
1     [any data]        value from attributes_values             value from attributes_values
2     [any data]        value from attributes_values             value from attributes_value

So far, my only way of accomplishing this is not very elegant as I'm having to do multiple joins to the same table: 到目前为止,我实现这一目标的唯一方法并不是很优雅,因为我必须对同一个表进行多次连接:

SELECT * FROM content AS a
LEFT JOIN attributes_values AS b ON (b.content_id = a.id AND b.attribute_id = [Model # ID])
LEFT JOIN attributes_values AS c ON (c.content_id = a.id AND c.attribute_id = [Part # ID])
WHERE a.id IN (
    SELECT content_id FROM attributes_values WHERE attribute_id = [Show in Cart ID] AND value = 1
)

As you can see, I'm having to hard-code related keys into the JOIN statements, which doesn't make this very scalable (and just feels dirty). 正如您所看到的,我不得不将相关键硬编码到JOIN语句中,这不会使其具有可扩展性(并且只是感觉很脏)。

Some last few notes: I'm using PHP and Joomla!, so if that would affect your answer (or if there's some secret Joomla tip), feel free to include it. 最后几个注意事项:我正在使用PHP和Joomla !,所以如果这会影响你的答案(或者如果有一些秘密的Joomla提示),请随意包含它。 Also, this is a pre-established table schema that I'm not able to alter, so I need a query solution for the above mentioned tables, not a suggestion on a better table design. 此外,这是一个我无法改变的预先建立的表模式,因此我需要一个针对上述表的查询解决方案,而不是更好的表设计的建议。

Any help will be greatly appreciated. 任何帮助将不胜感激。

Cheers! 干杯!

Instead of joining your table multiple times for each attribute, you can use an aggregated function like MAX() in combination with a CASE WHEN statement: 您可以使用MAX()等聚合函数与CASE WHEN语句结合使用,而不是为每个属性多次连接表:

SELECT
  content.id,
  [other_fields],
  MAX(CASE WHEN attributes.title='Model #' THEN attributes_values.value END) AS Model_N,
  MAX(CASE WHEN attributes.title='Part #' THEN attributes_values.value END) AS Part_N
FROM
  content INNER JOIN attributes_values
  ON content.id = attributes_values.content_id
  INNER JOIN attributes
  ON attributes_values.attribute_id = attributes.id
GROUP BY
  id,
  [other_fields]
HAVING
  MAX(CASE WHEN attributes.title='Show in Cart' THEN attribute_values.value END)='1'

a little explanation: 一点解释:

CASE WHEN attributes.title='Model #' THEN attributes.value END

will return the value when the attribute is 'Model #' and NULL otherwise, and 将在属性为“Model#”时返回值,否则返回NULL

MAX(...)

will return the maximum non-NULL value, which is the value where attributes.title='Model #'. 将返回最大的非NULL值,这是attributes.title ='Model#'的值。

dynamic attributes 动态属性

MySQL does not have a Pivot function, so if you want your list to be dynamic, you have to dinamically create a SQL string with all attributes and execute this string. MySQL没有Pivot函数,因此如果您希望列表是动态的,则必须使用所有属性创建一个包含所有属性的SQL字符串并执行此字符串。

You can fetch all attributes from the attributes table: 您可以从attributes表中获取所有属性:

SELECT
    CONCAT(
      'MAX(CASE WHEN attributes.title=''',
      REPLACE(attributes.title, '''', ''''''),
      ''' THEN attributes_values.value END) AS `',
      REPLACE(attributes.title, '`', '``'),
      '`'
    )
FROM
  attributes;

and combine everything together with GROUP_CONCAT: 并将所有内容与GROUP_CONCAT结合在一起:

SELECT GROUP_CONCAT(...the concat above...)
FROM attributes;

so your final query will be something like this: 所以你的最终查询将是这样的:

SELECT
  CONCAT('SELECT content.id,',

  GROUP_CONCAT(
    CONCAT(
      'MAX(CASE WHEN attributes.title=''',
      REPLACE(attributes.title, '''', ''''''),
      ''' THEN attributes_values.value END) AS `',
      REPLACE(attributes.title, '`', '``'),
      '`'
    )
  ),

  ' FROM content INNER JOIN attributes_values ',
  'ON content.id = attributes_values.content_id ',
  'INNER JOIN attributes ',
  'ON attributes_values.attribute_id = attributes.id ',
  'GROUP BY id HAVING ',
  'MAX(CASE WHEN attributes.title=''Show in Cart'' THEN attributes_values.value END)=''1'''
  )
FROM
  attributes
INTO @SQL;

now the @SQL variable will hold the value of the query to be executed, and you can execute it with: 现在@SQL变量将保存要执行的查询的值,您可以使用以下命令执行它:

PREPARE stmt FROM @sql;
EXECUTE stmt;

The only problem here is that GROUP_CONCAT has a maximum length limit , you can increase it if your table is holding many attributes. 这里唯一的问题是GROUP_CONCAT有一个最大长度限制 ,如果你的表持有许多属性,你可以增加它。

Please see an example fiddle here . 在此处查看示例小提琴。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM