MySQL:如何转换为 EAV - 第 3 部分?

发布于 2024-11-30 02:24:26 字数 1379 浏览 3 评论 0原文

以前的相关文章:

给定一个表:

TABLE: foo
===============================
| id | first_name | last_name |
===============================
| 1  | John       | Doe       |
| 2  | Jane       | Smith     |
| 3  | Ronald     | McDonald  |
-------------------------------

如何做我获取此表并将其转换为这些表(EAV 实现)?:

TABLE: attribute
===========================
| id | fk_id | attribute  |
===========================
| 1  | 100   | first_name |
| 2  | 100   | last_name  |
---------------------------

TABLE: value
=========================================
| id | attribute_id | row_id | value    |
=========================================
| 1  | 1            | 1      | John     |
| 2  | 2            | 1      | Doe      |
| 3  | 1            | 2      | Jane     |
| 4  | 2            | 2      | Smith    |
| 5  | 1            | 3      | Ronald   |
| 6  | 2            | 3      | McDonald |
-----------------------------------------

注意:

  • 将提供 attribute.fk_id。
  • value.row_id 用于标识如何将值分组为原始表中的记录。

更新:另外,如何查询 EAV 表,以便使其再次看起来像表 foo

Previous Related Posts:

Given a table:

TABLE: foo
===============================
| id | first_name | last_name |
===============================
| 1  | John       | Doe       |
| 2  | Jane       | Smith     |
| 3  | Ronald     | McDonald  |
-------------------------------

How do I take this table and convert it to these tables (an EAV implementation)?:

TABLE: attribute
===========================
| id | fk_id | attribute  |
===========================
| 1  | 100   | first_name |
| 2  | 100   | last_name  |
---------------------------

TABLE: value
=========================================
| id | attribute_id | row_id | value    |
=========================================
| 1  | 1            | 1      | John     |
| 2  | 2            | 1      | Doe      |
| 3  | 1            | 2      | Jane     |
| 4  | 2            | 2      | Smith    |
| 5  | 1            | 3      | Ronald   |
| 6  | 2            | 3      | McDonald |
-----------------------------------------

NOTES:

  • attribute.fk_id will be provided.
  • value.row_id is used to identify how the values are grouped as records in the original table.

UPDATE: Also, how do I query the EAV tables so that I can make it look like table foo again.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

缘字诀 2024-12-07 02:24:26

我对@Phil 用于填充 EAV 表的解决方案给予+1。一次插入一个属性。

这是反转 EAV 转换的另一种解决方案:

SELECT v.row_id AS id,
  MAX(IF(a.attribute='first_name',v.value,NULL)) AS first_name,
  MAX(IF(a.attribute='last_name',v.value,NULL)) AS last_name
FROM value v INNER JOIN attribute a
  ON v.attribute_id = a.id 
GROUP BY v.row_id

除了使用 EAV 之外,您已将所有值放入 VARCHAR(255) 列或其他列中,因此您丢失了有关原始列各自数据类型的信息。

如果不对属性名称进行硬编码,无论是像 @Phil 所示的连接,还是像我所示的列,实际上没有办法“动态”地做到这一点。这本质上与尝试编写动态数据透视查询相同的问题。

我在演讲中写了更多关于 EAV 的内容 实用面向对象模型在 SQL 和我的书中,SQL 反模式卷 1:避免数据库编程的陷阱

I give +1 to @Phil's solution for populating the EAV table. Insert one attribute at a time.

Here's another solution to reverse an EAV transformation:

SELECT v.row_id AS id,
  MAX(IF(a.attribute='first_name',v.value,NULL)) AS first_name,
  MAX(IF(a.attribute='last_name',v.value,NULL)) AS last_name
FROM value v INNER JOIN attribute a
  ON v.attribute_id = a.id 
GROUP BY v.row_id

Except that by using EAV, you've put all your values into a column of VARCHAR(255) or whatever, so you have lost information about the respective data types of the original columns.

There's really no way to do it "dynamically" without hard-coding the attribute names, either as joins as @Phil shows, or as columns as I show. It's essentially the same problem as trying to write dynamic pivot queries.

I have written more about EAV in my presentation Practical Object-Oriented Models in SQL and in my book, SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.

陌若浮生 2024-12-07 02:24:26

我认为您唯一的希望是使用 foo 表。如果没有 ID,bar 基本上就没用了。

尝试这样的事情(假设attribute.id是一个自动递增主键)

INSERT INTO `attribute` (`fk_id`, `attribute`)
VALUES (100, 'first_name');

INSERT INTO `value` (`attribute_id`, `row_id`, `value`)
SELECT LAST_INSERT_ID(), `id`, `first_name`
FROM `foo`;

INSERT INTO `attribute` (`fk_id`, `attribute`)
VALUES (100, 'last_name');

INSERT INTO `value` (`attribute_id`, `row_id`, `value`)
SELECT LAST_INSERT_ID(), `id`, `last_name`
FROM `foo`;

要重建foo表,尝试这个

SELECT
    `fn`.`row_id` AS `id`,
    `fn`.`value` AS `first_name`,
    `ln`.`value` AS `last_name`
FROM `value` `fn`
INNER JOIN `attribute` `fn_a`
    ON `fn`.`attribute_id` = `fn_a`.`id`
    AND `fn_a`.`attribute` = 'first_name'
INNER JOIN `value` `ln`
    ON `fn`.`row_id` = `ln`.`row_id`
INNER JOIN `attribute` `ln_a`
    ON `ln`.`attribute_id` = `ln_a`.`id`
    AND `ln_a`.`attribute` = 'last_name'

呃,谢谢你提醒我为什么我讨厌这种模式

I think your only hope is if you use the foo table. bar is essentially useless without the ID.

Try something like this (assuming attribute.id is an auto-increment primary key)

INSERT INTO `attribute` (`fk_id`, `attribute`)
VALUES (100, 'first_name');

INSERT INTO `value` (`attribute_id`, `row_id`, `value`)
SELECT LAST_INSERT_ID(), `id`, `first_name`
FROM `foo`;

INSERT INTO `attribute` (`fk_id`, `attribute`)
VALUES (100, 'last_name');

INSERT INTO `value` (`attribute_id`, `row_id`, `value`)
SELECT LAST_INSERT_ID(), `id`, `last_name`
FROM `foo`;

To reconstruct the foo table, try this

SELECT
    `fn`.`row_id` AS `id`,
    `fn`.`value` AS `first_name`,
    `ln`.`value` AS `last_name`
FROM `value` `fn`
INNER JOIN `attribute` `fn_a`
    ON `fn`.`attribute_id` = `fn_a`.`id`
    AND `fn_a`.`attribute` = 'first_name'
INNER JOIN `value` `ln`
    ON `fn`.`row_id` = `ln`.`row_id`
INNER JOIN `attribute` `ln_a`
    ON `ln`.`attribute_id` = `ln_a`.`id`
    AND `ln_a`.`attribute` = 'last_name'

Ergh, thanks for reminding me why I hate this pattern

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文