products
+----+--------+
| id | title |
+----+--------+
| 1 | Apple |
| 2 | Pear |
| 3 | Banana |
| 4 | Tomato |
+----+--------+
product_variants
+----+------------+------------+
| id | product_id | is_default |
+----+------------+------------+
| 1 | 1 | 0 |
| 2 | 1 | 1 |
| 3 | 2 | 1 |
| 4 | 3 | 1 |
| 5 | 4 | 1 |
+----+------------+------------+
properties
+----+-----------------+-----------+
| id | property_key_id | value |
+----+-----------------+-----------+
| 1 | 1 | Yellow |
| 2 | 1 | Green |
| 3 | 1 | Red |
| 4 | 2 | Fruit |
| 5 | 2 | Vegetable |
| 6 | 1 | Blue |
+----+-----------------+-----------+
property_keys
+----+-------+
| id | value |
+----+-------+
| 1 | Color |
| 2 | Type |
+----+-------+
product_has_properties
+----+------------+-------------+
| id | product_id | property_id |
+----+------------+-------------+
| 1 | 1 | 4 |
| 2 | 1 | 3 |
| 3 | 2 | 4 |
| 4 | 3 | 4 |
| 5 | 3 | 4 |
| 6 | 4 | 4 |
| 7 | 4 | 5 |
+----+------------+-------------+
product_variant_has_properties
+----+------------+-------------+
| id | variant_id | property_id |
+----+------------+-------------+
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 2 | 6 |
| 4 | 3 | 4 |
| 5 | 4 | 1 |
| 6 | 5 | 1 |
+----+------------+-------------+
我需要查询我的数据库,以便选择products
,其中某些properties
附加到产品本身或将这些属性附加到其相关的{{1} }}。同样应将具有相同properties.property_key_id的product_variants
分组为:properties
示例案例:
(pkey1='red' OR pkey1='blue') AND (pkey2='fruit' OR pkey2='vegetable')
的所有产品。这应该只返回番茄。(color='red' AND type='vegetable')
的所有产品应返回Apple和Banana 请注意,在上面的示例中我不需要通过properties.value查询,我可以通过properties.id进行查询。
我使用MySQL查询玩了很多,但我遇到的最大问题是通过两个数据透视表加载的属性。加载它们没有问题,但加载它们并将它们与正确的((color='red' OR color='yellow') AND type='fruit')
,WHERE
和AND
语句组合起来就是。
答案 0 :(得分:9)
以下代码应该为您提供您正在寻找的内容,但是您应该注意到您的表格当前有一个黄色和蔬菜列表的番茄。显然你希望番茄是红色的而番茄实际上是一种不是蔬菜的水果:
Select distinct title
from products p
inner join
product_variants pv on pv.product_id = p.id
inner join
product_variant_has_properties pvp on pvp.variant_id = pv.id
inner join
product_has_properties php on php.product_id = p.id
inner join
properties ps1 on ps1.id = pvp.property_id --Color
inner join
properties ps2 on ps2.id = php.property_id --Type
inner join
property_keys pk on pk.id = ps1.property_key_id or pk.id = ps2.property_key_id
where ps1.value = 'Red' and ps2.value = 'Vegetable'
答案 1 :(得分:3)
这是一个令人费解的答案,有可能以更简单的方式进行。但是,鉴于您似乎希望能够通过color = xx
和type = xx
进行查询,我们显然需要包含这些名称的列,正如您所暗示的那样,这意味着我们需要转动数据
此外,由于我们希望获得每种产品的颜色和类型的所有组合,我们需要执行一种交叉连接,以组合它们。
这引导我们进行查询 - 首先我们获得产品及其变体的所有类型,然后我们将其加入产品及其变体的所有颜色。我们使用union
来合并产品和变体属性,以便将它们全部保存在同一列中,而不是要检查多个列。
当然,所有产品都可能没有指定此信息,因此我们一直使用left joins
。如果确保产品始终至少有一种颜色,并且至少有一种颜色 - 它们都可以更改为inner joins
。
另外,在您的示例中,您说tomato
的颜色应为red
,但在您提供的示例数据中,我确保tomato
的颜色为{ {1}}。
无论如何,这是查询:
yellow
这是一个演示:http://sqlfiddle.com/#!9/d3ded/76
如果要获得更多类型的属性,除了颜色和类型之外,还需要修改查询 - 抱歉,但这几乎是你所困扰的,试图转入MySQL的
答案 2 :(得分:2)
我会对你得到的那些答案略有不同。虽然很可能有一个纯粹的SQL答案,但我会向你提出的问题是:为什么?
该答案将决定您的下一步。
如果你的答案是尝试学习纯粹的SQL方法,那么这里有一些很好的答案,如果不是全部的那样,你可以获得最多的答案。
如果您的答案是为最终应用程序创建可扩展的动态查询,那么您可以通过依靠编程语言来缓解您的工作。
我需要使用更多表来转移数据。我决心以最好的方式尝试这样做,而且我花了很多时间研究出最适合我应用的方法。完全了解这可能与您的体验不一样,我会在这里分享我的经验以防万一。
我尝试创建纯SQL解决方案,这些解决方案适用于特定用例,但需要对每个其他用例进行大量调整。当我尝试扩展查询时,我首先尝试创建存储过程。这是一场噩梦,在我的开发过程中很早就意识到这将是一个令人头痛的问题。
我继续使用PHP并创建自己的查询生成。虽然这些代码中的一些已经演变成对我来说非常有用的东西,但我了解到,除非我创建服务库,否则维护很多都会很困难。那时,我意识到我基本上将创建一个对象关系映射器(ORM)。除非我的应用程序 SO 特殊且 SO 唯一,否则市场上没有任何ORM可以接近我做的事情我想,然后我需要借此机会探索为我的应用程序使用ORM。尽管我最初的保留让我做了所有事情但是看了一下ORM,我已经开始使用它了,这有助于我的开发速度显着提高。
Select all products with (color='red' AND type='vegetable'). This should return only Tomato.
Select all products with ((color='red' OR color='yellow') AND type='fruit') should return Apple and Banana
这可以在ORM中实现。您所描述的内容只是在SQL中松散定义,但实际上在OOP中已经完美地概括了。这就是它在PHP中的样子,仅作为一个例子。
<?
Abtract class AbstractProductType {
public function __construct() {
}
}
class Color extends AbstractProductType {
}
class Yellow extends Color {
}
class Red extends Color {
}
class Type extends AbstractProductType {
}
class Vegetable extends Type {
}
class Fruit extends Type {
}
class Product {
public function setColor(Color $color) {
//
}
public function setType(Type $type) {
//
}
}
$product = new Product();
$product->setColor(new Red());
$product->setType(new Fruit());
$result = $product->find();
?>
这背后的想法是你可以在面向对象的编程中充分利用SQL。
稍微低一点的版本是创建一个生成SQL片段的类。我个人的经验是,有限的回报是很多工作。如果你的项目将保持相对较小,它可能会很好。但是,如果你反驳说你的项目会增长,那么ORM可能值得探索。
虽然我不确定您将使用什么语言来查询和操纵您的数据,但是有很棒的ORM不应该打折扣。尽管他们有许多缺点(你可以在互联网上找到很多关于这方面的争论),但我不情愿地相信,尽管对于所有情况肯定不是理想的,但是他们应该考虑一些。如果这不适合您,请准备好自己写出很多JOIN
个。当引用表n
次并且需要引用回表时,我知道添加引用的唯一方法是创建n
JOIN
s。
当然,我会非常有兴趣看看是否有更好的方法!
答案 3 :(得分:2)
条件聚合
您可以在having
子句中使用条件聚合来查看产品是否具有特定属性。例如,查询同时具有“type vegetable”和“color red”属性的所有产品。
您必须按产品ID和产品变体ID进行分组,以确保您搜索的所有属性都存在于同一变体或产品本身。
select p.id, pv.id from products p
left join product_has_properties php on php.product_id = p.id
left join properties pr on pr.id = php.property_id
left join property_keys pk on pk.id = pr.property_key_id
left join product_variants pv on pv.product_id = p.id
left join product_variant_has_properties pvhp on pvhp.variant_id = pv.id
left join properties pr2 on pr2.id = pvhp.property_id
left join property_keys pk2 on pk2.id = pr2.property_id
group by p.id, pv.id
having (
count(case when pk.value = 'Color' and pr.value = 'Red' then 1 end) > 0
and count(case when pk.value = 'Type' and pr.value = 'Vegetable' then 1 end) > 0
) or (
count(case when pk2.value = 'Color' and pr2.value = 'Red' then 1 end) > 0
and count(case when pk2.value = 'Type' and pr2.value = 'Vegetable' then 1 end) > 0
)
答案 4 :(得分:2)
问题是什么?(我多次阅读该帖子,而且我仍然没有看到任何实际的问题。)这里的很多答案似乎都在回答问题&#34;什么SQL语句会从这些表中返回结果?&#34;我的回答并没有提供一个例子或者&#34;如何&#34;编写SQL的指南。我的回答解决了一个根本不同的问题。
OP正在经历针对&#34;问题&#34;中显示的表编写SQL的困难。是由于(我所说的)阻抗不匹配&#34;在&#34;关系之间&#34;模型和&#34;实体 - 属性 - 值&#34; (EAV)模型。
SQL旨在与&#34;关系&#34;模型。实体的每个实例都表示为元组,在表中存储一行。实体的属性作为值存储在实体行的列中。
EAV模型与Relational模型有很大不同。它将属性值移出实体行,并将它们移动到其他表中的多个单独行中。如果查询试图模仿查询&#34;关系&#34;那么这会使编写查询变得更加复杂。通过转换来自&#34; EAV&#34;表示回到&#34;关系&#34;表示。
有几种针对EAV模型编写SQL查询的方法,它模拟从Relational模型返回的结果(正如其他答案中提供的示例SQL所证明的那样&#34;问题&#34;。
一种方法是在SELECT
列表中使用子查询,将属性值作为实体行中的列返回。
另一种方法是在实体表中的行与属性表中的行之间执行连接,并使用GROUP BY
折叠行,并在SELECT
列表中,使用条件表达式&#34;挑选&#34;要为列返回的值。
这两种方法都有很多例子。并且两者都不比另一种好,每种方法的适用性实际上取决于具体的用例。
虽然可以针对显示的EAV样式表编写SQL查询,但这些查询比对&#34;关系&#34中存储的数据的等效查询的数量级更复杂 ;模型。
关系模型中的普通查询返回的结果,例如
SELECT p.id
FROM product p
WHERE p.color = 'red'
要从EAV模型中的数据返回同一组,需要更复杂的SQL查询,涉及多个表和/或子查询的连接。
一旦我们超越了普通的查询,我们想要从多个相关实体返回属性的查询...作为一个简单的例子,返回过去30天内产品的信息。红色&#39;
SELECT c.customer_name
, c.address
, o.order_date
, p.product_name
, l.qty
FROM customer c
JOIN order o ON ...
JOIN line_item l ON ...
JOIN product p ON ...
WHERE p.color = 'red'
AND o.order_date >= DATE(NOW()) + INTERVAL 30
从EAV模型中使用SQL得到相同的结果 way 更加复杂和令人困惑,并且可能是令人难以忍受的挫败感。
当然,可以编写SQL。一旦我们设法获得可以返回&#34;正确&#34;的SQL语句。结果集,当表中的行数超出平凡演示时,直到我们期望数据库处理的卷类型......这些查询的性能是可怕的(与从传统返回相同结果的查询相比)关系模型)。
(我们甚至没有涉及仅添加和更新实体属性,强制实体之间的参照完整性等的额外复杂性。)
但为什么我们想要这样做? 为什么我们是否需要(或想要)针对EAV模型表编写SQL语句,这些模型表模拟从对关系模型表的查询返回的结果?
最重要的是,如果我们打算使用EAV模型,那么不试图使用单个SQL语句返回结果,就像我们从查询中返回一样,我们会好得多一个&#34;关系&#34;模型。
从EAV模型中检索信息的问题更适合于面向对象的编程语言,并提供了一个框架。在SQL中完全结合的东西。