【问题标题】:Query by Value in Adjacent Column in MySQL在 MySQL 中按相邻列中的值查询
【发布时间】:2012-11-09 00:28:14
【问题描述】:

我相信所有这种性质的问题都是这样开始的...... 我有两张桌子fieldsdataFields 描述(不存在)表的列名,data 包含该(不存在)表的数据。就这样……

字段:

    +----+---------+-------------+---------------+-------+----------+
    | ID | F_ORDER | NAME        | LABEL         | VALUE | TYPE     |
    +----+---------+-------------+---------------+-------+----------+
    |  1 |       1 | IS_EMPLOYEE | Region        |       | checkbox |
    |  2 |       3 | EM_AVATAR   | Avatar        |       | avatar   |
    |  3 |       4 | EM_JOBTITLE | Job Title     |       | text     |
    |  4 |       5 | EM_COMPANY  | Company       |       | text     |
    |  5 |       6 | EM_PHONE    | Phone         |       | text     |
    |  6 |       2 | EM_ORDER    | Display Order | 5     | text     |
    +----+---------+-------------+---------------+-------+----------+

数据:

    +-----+----------+---------+--------------------------------------+
    | ID  | FIELD_ID | USER_ID | VALUE                                |
    +-----+----------+---------+--------------------------------------+
    |   5 |        1 |       1 | YES                                  |
    |   6 |        2 |       1 |                                      |
    |   7 |        3 |       1 | Owner                                |
    |   8 |        4 |       1 | Acme, Inc.                           |
    |   9 |        5 |       1 | 123-456-7987                         |
    | 150 |        5 |      31 | 123-623-5555                         |
    | 149 |        4 |      31 | Acme, Inc.                           |
    | 148 |        3 |      31 | Sales and Customer Support           |
    | 147 |        2 |      31 |                                      |
    | 146 |        1 |      31 | YES                                  |
    |  26 |        1 |       6 | NO                                   |
    |  27 |        2 |       6 | http://example.com/avi/avi.jpeg      |
    |  28 |        3 |       6 | CEO                                  |
    |  29 |        4 |       6 | Acme                                 |
    |  30 |        5 |       6 | (123) 734-5555                       |
    |  31 |        1 |       7 | NO                                   |
    |  32 |        2 |       7 | http://example.com/avi/avi.jpeg      |
    |  33 |        3 |       7 | VP, Services                         |
    |  34 |        4 |       7 | Acme                                 |
    |  35 |        5 |       7 | (913) 963-5555                       |
    |  36 |        1 |      14 | NO                                   |
    |  37 |        2 |      14 | http://example.com/avi/avi.jpeg      |
    |  38 |        3 |      14 | Senior Accountant                    |
    |  39 |        4 |      14 | Acme                                 |
    |  40 |        5 |      14 | (123) 213-5555                       |
    |  41 |        1 |      10 | NO                                   |
    |  42 |        2 |      10 | http://example.com/avi/avi.jpeg      |
    |  43 |        3 |      10 | President                            |
    |  44 |        4 |      10 | Acme                                 |
    |  45 |        5 |      10 | (123) 734-5555                       |
    |  46 |        1 |      12 | NO                                   |
    |  47 |        2 |      12 | http://example.com/avi/avi.jpeg      |
    |  48 |        3 |      12 | Services Supervisor                  |
    |  49 |        4 |      12 | Acme                                 |
    |  50 |        5 |      12 | (123) 573-5555                       |
    |  51 |        1 |      11 | NO                                   |
    |  52 |        2 |      11 | http://example.com/avi/avi.jpeg      |
    |  53 |        3 |      11 | Operations Supervisor                |
    |  54 |        4 |      11 | Acme                                 |
    |  55 |        5 |      11 | (123) 259-5555                       |
    |  56 |        1 |       8 | NO                                   |
    |  57 |        2 |       8 | http://example.com/avi/avi.jpeg      |
    |  58 |        3 |       8 | General Information                  |
    |  59 |        4 |       8 | Acme                                 |
    |  60 |        5 |       8 | (123) 213-5555                       |
    |  61 |        1 |       9 | NO                                   |
    |  62 |        2 |       9 | http://example.com/avi/avi.jpeg      |
    |  63 |        3 |       9 | VP, Sales                            |
    |  64 |        4 |       9 | Acme                                 |
    |  65 |        5 |       9 | (123) 210-5555                       |
    +-----+----------+---------+--------------------------------------+

我要查找的查询的基本用语是:我想要所有员工 (IS_EMPLOYEE = "YES") 的所有信息,并按他们的显示顺序列 (EM_ORDER) 排序。

到目前为止,我的查询让我无处可去。我得到的结果有点像这样:

    +--------+------------+------------+-------+---------+-------+
     ID        FIELD_ID    NAME         LABEL   TYPE       VALUE
    +--------+------------+------------+-------+---------+-------+
     7         1           IS_EMPLOYEE  Region  checkbox   YES
    +--------+------------+------------+-------+---------+-------+

我需要的是这样的结果:

    +-------+------------+---------+-----------+----------+-------------+--------+
     USER_ID IS_EMPLOYEE  EM_AVATAR EM_JOBTITLE EM_COMPANY EM_PHONE      EM_ORDER   
    +-------+------------+---------+-----------+----------+-------------+--------+
     6       YES           http://  CEO         Acme       123-123-555   5 
    +-------+------------+---------+-----------+----------+-------------+--------+ 

当然,我正在尝试将其全部返回 PHP 作为可用数组($results['user_id']['jobtitle'] 等)。我可以得到 everything 并使用 PHP 完成它,但我正在尝试学习 MySQL,我认为这是一种比执行几个 foreach 块更快的方法......虽然我可能是错的。

提前感谢您的帮助。

【问题讨论】:

  • 数据表可以对同一个字段同一个用户有多个值吗? (即:你的基数是多少?)
  • 感谢您指出可能的重复。我整天都在这里寻找想法。但是当你真的,真的不知道你在寻找什么类型的解决方案时,很难发现它们。

标签: mysql select join union entity-attribute-value


【解决方案1】:

您的数据表以Entity-Attribute-Value 方式组织,这不是存储关系数据的有效方式。难怪用 SQL 将其检索到每个属性一列的常规行中会很尴尬。

要使用 SQL 执行此操作,您基本上需要执行 数据透视查询。这很难写,执行起来效率极低。

最好为员工获取所有数据:

SELECT e.*, f.label
FROM Data AS is_employee
JOIN Data AS e USING (user_id)
JOIN Fields AS f ON e.field_id = f.id
WHERE (is_employee.field_id, is_employee.value) = (1, 'Yes');

然后在 PHP 代码中将其重组为关联数组的集合:

while ($row = $stmt->fetch()) {
  $data[ $row['user_id'] ][ $row['label'] ] = $row['value'];
}

【讨论】:

  • 这是一个非常有帮助的答案。谢谢你。我同意这个组织很糟糕,即使从我的新手的角度来看也是如此。但此时我无法改变结构。我可能不得不承认失败——我想知道何时这样做是关键。
【解决方案2】:

这基本上是 PIVOT,但 MySQL 没有 PIVOT 函数,因此您可以使用聚合函数和 CASE 语句复制它。

静态版本是指您知道要转换为列的所有值(这些是您的字段名称):

select 
  d.user_id,
  max(case when f.name = 'IS_EMPLOYEE' then d.value else null end) IS_EMPLOYEE,
  max(case when f.name = 'EM_AVATAR' then d.value else null end) EM_AVATAR,
  max(case when f.name = 'EM_JOBTITLE' then d.value else null end) EM_JOBTITLE,
  max(case when f.name = 'EM_COMPANY' then d.value else null end) EM_COMPANY,
  max(case when f.name = 'EM_PHONE' then d.value else null end) EM_PHONE,
  max(case when f.name = 'EM_ORDER' then d.value else null end) EM_ORDER
from data d
left join fields f
  on f.id = d.FIELD_ID
group by d.user_id

SQL Fiddle with Demo

结果:

| USER_ID | IS_EMPLOYEE |                       EM_AVATAR |                EM_JOBTITLE | EM_COMPANY |       EM_PHONE | EM_ORDER |
---------------------------------------------------------------------------------------------------------------------------------
|       1 |         YES |                          (null) |                      Owner | Acme, Inc. |   123-456-7987 |   (null) |
|       6 |          NO | http://example.com/avi/avi.jpeg |                        CEO |       Acme | (123) 734-5555 |   (null) |
|       7 |          NO | http://example.com/avi/avi.jpeg |               VP, Services |       Acme | (913) 963-5555 |   (null) |
|       8 |          NO | http://example.com/avi/avi.jpeg |        General Information |       Acme | (123) 213-5555 |   (null) |
|       9 |          NO | http://example.com/avi/avi.jpeg |                  VP, Sales |       Acme | (123) 210-5555 |   (null) |
|      10 |          NO | http://example.com/avi/avi.jpeg |                  President |       Acme | (123) 734-5555 |   (null) |
|      11 |          NO | http://example.com/avi/avi.jpeg |      Operations Supervisor |       Acme | (123) 259-5555 |   (null) |
|      12 |          NO | http://example.com/avi/avi.jpeg |        Services Supervisor |       Acme | (123) 573-5555 |   (null) |
|      14 |          NO | http://example.com/avi/avi.jpeg |          Senior Accountant |       Acme | (123) 213-5555 |   (null) |
|      31 |         YES |                          (null) | Sales and Customer Support | Acme, Inc. |   123-623-5555 |   (null) |

如果您有未知数量的值要转换为列,那么您可以使用准备好的语句来动态生成 sql。

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'max(case when f.name = ''',
      name,
      ''' then d.value end) AS ',
      name
    )
  ) INTO @sql
FROM fields;

SET @sql = CONCAT('SELECT d.user_id, ', @sql, ' 
                  from data d
                  left join fields f
                    on f.id = d.FIELD_ID
                   GROUP BY d.user_id');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

SQL Fiddle with Demo

两者都会产生相同的结果。

然后您可以添加一个WHERE 子句来过滤掉任何不需要的行。

【讨论】:

  • 只要知道如果每个字段/用户有多个值,这将无法正常工作。
  • @marvin 但是,如果每个用户有多个记录,则有一些方法可以实现这种类型的查询。
  • @Shelton 它伴随着练习。 :) 这当然可以根据您的任何需求进行调整,我只是提供了一个包含完整数据集的示例。
  • @Shelton 那么您不必担心每个用户会丢失记录,因为不能有多个条目。
  • 为了安全起见,我会添加一些 RI 以在数据库中强制执行它。 (你是快 BF)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-04
  • 2018-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多