规范化,聚合和连接表问题

时间:2016-08-02 21:45:00

标签: sql sql-server

假设我有一个客户数据库,他们购买的材料是适用的"随机对象。例如,约翰买了10美元的材料X"适用于汽车和房屋。

Customers
+----+-------+
| ID | Name  |
+----+-------+
|  1 | John  |
|  2 | Larry |
+----+-------+

Orders
+---------+------------+-------+----------+
| OrderID | CustomerID | Sales | Material |
+---------+------------+-------+----------+
|       1 |          1 |    10 | x        |
|       2 |          1 |    15 | x        |
|       3 |          1 |     6 | y        |
|       4 |          2 |     3 | x        |
|       5 |          2 |    25 | y        |
+---------+------------+-------+----------+

我的Materials表最初看起来像这样

+----------+-------------------------+
| Material |      Applicability      |
+----------+-------------------------+
| x        | car, house, plane, bike |
| y        | car, bike               |
+----------+-------------------------+

当我需要显示John购买的材料以及材料适用的对象时,我的查询是这样的。

Select ID, Name, sum(Sales), Material, Applicability
FROM Customers a 
INNER JOIN Orders b on a.ID = b.CustomerID
INNER JOIN Materials c on b.Material = c.Material
WHERE Name = 'John' 
GROUP BY ID, Name, Material, Applicability

结果

+----+------+--------------+----------+-------------------------+
| ID | Name | Total Sales | Material |      Applicability      |
+----+------+--------------+----------+-------------------------+
|  1 | John |           25 | x        | car, house, plane, bike |
|  1 | John |            6 | y        | car, bike               |
+----+------+--------------+----------+-------------------------+

逗号分隔值(我知道它违反了许多规则)很方便,因为在解析适用性时,我可以简单地用逗号分割字符串,然后我有一个适用性对象列表。

现在已经决定对Materials表进行规范化,所以现在它看起来像这样

+----------+---------------+
| Material | Applicability |
+----------+---------------+
| x        | car           |
| x        | house         |
| x        | plane         |
| x        | bike          |
| y        | car           |
| y        | bike          |
+----------+---------------+

此规范化已中断我现有的查询,导致sum(sales)结果为材料适用的多个对象的倍数。

实施例

+----+------+-------------+----------+---------------+
| ID | Name | Total Sales | Material | Applicability |
+----+------+-------------+----------+---------------+
|  1 | John |          25 | x        | car           |
|  1 | John |          25 | x        | house         |
|  1 | John |          25 | x        | plane         |
|  1 | John |          25 | x        | bike          |
|  1 | John |           6 | y        | car           |
|  1 | John |           6 | y        | bike          |
+----+------+-------------+----------+---------------+

现在看来John已经购买了100美元的材料x,当时他真的只买了25美元。我需要向用户显示约翰购买材料x以及x的适用性。

主要问题是当我需要找出约翰购买的东西时,还要按适用性进行过滤。

Select ID, Name, sum(Sales), Material, Applicability
FROM Customers a 
INNER JOIN Orders b on a.ID = b.CustomerID
INNER JOIN Materials c on b.Material = c.Material
WHERE Name = 'John' and (applicability = 'car' or applicability = 'bike')
GROUP BY ID, Name, Material, Applicability

如果任何材料适用于汽车和自行车,那么总价值sum(sales)将加倍。

我如何处理这种重复?

2 个答案:

答案 0 :(得分:0)

最简单的方法(对原始查询的修改最少)可以先加总,然后加入适用性:

; with CTE as (
    Select ID, Name, sum(Sales) as TotalSales, material
    From Customers a
    inner join orders b
    on a.ID = b.CustomerID
    group by ID, Name, Material
    )

select b.*, c.Applicability from CTE b
inner join Materials c on b.Material = c.Material
where...--insert selection criteria here

答案 1 :(得分:0)

SELECT  'x' as Material       , 'car'   as Applicability         
INTO #Materials 
UNION ALL
SELECT 'x'       , 'house'        
UNION ALL
SELECT 'x'       , 'plane'       
UNION ALL
SELECT 'x'       , 'bike'         
UNION ALL
SELECT'y'      , 'car'          
UNION ALL
SELECT 'y'       , 'bike'  





SELECT    1 as OrderID ,          1  as CustomerID,    10  as Sales, 'x'  as    Material   
INTO #Orders
UNION ALL
SELECT    2 ,          1 ,    15 , 'x'        
UNION ALL
SELECT    3 ,          1 ,     6 , 'y'        
UNION ALL
SELECT    4 ,          2 ,     3 , 'x'        
UNION ALL
SELECT    5 ,          2 ,    25 , 'y'        



SELECT   1 as ID ,  'John'  as Name
INTO #Customers
UNION ALL
SELECT 2 , 'Larry';
with CTE as (
SELECT ID, Name, sum(Sales) as TotalSales, c.material, Applicability
From #Customers a
inner join #orders b
on a.ID = b.CustomerID
inner join #Materials as c on c.Material = b.Material
where Name = 'John' and (applicability = 'car' or applicability = 'house')
group by ID, Name, c.Material, Applicability
)


SELECT ID, Name, TotalSales, Material, STUFF(
(SELECT distinct ',' +   Applicability 
FROM cte as c 
where c.Material = c1.Material

FOR XML PATH (''))
, 1, 1, '') as Applicability
from CTE c1
group by ID, Name, TotalSales, Material

drop table #Customers
drop table #Orders
drop table #Materials

希望这就是你要找的东西。