从具有多对多关系的多个表中进行选择

时间:2014-01-13 23:58:04

标签: sql sql-server tsql

大家好,我已经传承了一个设计不佳的数据库,我需要从3个表中获取一些信息

特许

  Id(Int, PK)
  FrID (varchar(50))
  FirstName (varchar(50))
  LastName (varchar(50))

商品

 Id (Int, PK) 
 FrID (varchar(50))
 StoreNumber (varchar(50))
 StoreName 
 Address 

定价

 Id (int, PK)
 StoreNumber  (varchar(50))
 Price1
 Price2
 Price3 

和数据

ID, FrID ,FirstName,LastName   
 1, 10   ,John Q    , TestCase  
 2, 10   ,Jack Q    , TestCase  
 3, 11   ,Jack Q    , TestCase


ID, FrID, StoreNumber , StoreName , Address 
10, 10  , 22222       , TestStore1, 123 Main street 
11, 10  , 33333       , TestStore2, 144 Last Street
12, 10  , 44444       , TestStore2, 145 Next Street
13, 11  , 55555       , Other Test, 156 Other st


ID, StoreNumber, Price1, Price2, Price3 
1,  22222      ,  19.99, 20.99 , 30.99 
2,  33333      ,  19.99, 20.99 , 30.99 
3,  44444      ,  19.99, 20.99 , 30.99 
4,  55555      ,  19.99, 20.99 , 30.99 

这就是我所做的

 SELECT F.FirstName,F.LastName,F.FrID , S.StoreNumber,S.StoreName,S.Address,
        P.Price1,P.Price2,P.Price3
 FROM Franchisee F 
 JOIN Store S on F.FrID = S.FrID 
 JOIN Pricing P on P.StoreNumber = S.StoreNumber 

这部分有效,但我最终得到了很多重复,例如Jack Q被列为他的商店以及John Q所在的每家商店。无论如何都要通过数据库重新设计来解决这个问题。

1 个答案:

答案 0 :(得分:1)

好的,有一整套问题清单,例如[FrId]等字符字段用作字符串,[地址]等保留字用作名称等。

让我们把糟糕的设计问题放在一边。

首先,我需要创建一个快速测试环境。我没有输入外键,因为不需要该约束来获得正确的答案。

--
-- Setup test tables
--


-- Just playing
use Tempdb;
go

-- drop table
if object_id('franchise')>  0
drop table franchise;
go

-- create table
create table franchise
( 
  Id int primary key,
  FrID varchar(50),
  FirstName varchar(50),
  LastName varchar(50)
);

-- insert data
insert into franchise values
( 1, 10, 'John Q', 'TestCase'),  
( 2, 10, 'Jack Q', 'TestCase'),
( 3, 11, 'Jack Q', 'TestCase');

-- select data
select * from franchise;
go


-- drop table
if object_id('store')>  0
drop table store;
go

-- create table
create table store
( 
  Id int primary key,
  FrID varchar(50),
  StoreNumber varchar(50),
  StoreName varchar(50),
  Address varchar(50)
);

-- insert data
insert into store values
(10, 10, 22222, 'TestStore1', '123 Main street'),
(11, 10, 33333, 'TestStore2', '144 Last Street'),
(12, 10, 44444, 'TestStore2', '145 Next Street'),
(13, 11, 55555, 'Other Test', '156 Other Street');

-- select data
select * from store;
go


-- drop table
if object_id('pricing')>  0
drop table pricing;
go

-- create table
create table pricing
( 
  Id int primary key,
  StoreNumber varchar(50),
  Price1 money,
  Price2 money,
  Price3 money
);


-- insert data
insert into pricing values
(1,  22222,  19.99, 20.99 , 30.99),
(2,  33333,  19.99, 20.99 , 30.99), 
(3,  44444,  19.99, 20.99 , 30.99), 
(4,  55555,  19.95, 20.95 , 30.95);

-- select data
select * from pricing;
go

主要问题是特许经营权表应该在FrId上有主键(PK),而不是Id。我不明白为什么有重复。

但是,下面的查询会通过分组删除它们。我更改了Jack Q的定价数据,以显示它是一个不同的记录。

--
-- Fixed Query - Version 1
--

select 
  f.FirstName, 
  f.LastName, 
  f.FrID, 
  s.StoreNumber, 
  s.StoreName, 
  s.Address,
  p.Price1,
  p.Price2, 
  p.Price3
from

-- Remove duplicates from francise
(
select 
  LastName,
  FirstName,
  Max(FrID) as FrID
from
  franchise
group by
  LastName,
  FirstName
) as f

join store s on f.FrID = s.FrID 
join pricing p on p.StoreNumber = s.StoreNumber;

正确的输出如下。

enter image description here

如果我没错,请删除重复条目并更改主键。

更改要求

好的,您将两个或更多所有者放在同一张桌子中。

下面使用子查询将所有者列表合并为一个字符串。另一种方法是拥有一个名为主要所有者的标志。选择它作为显示名称。

--
-- Fixed Query - Version 2
--

select 
  f.OwnersList,
  f.FrID, 
  s.StoreNumber, 
  s.StoreName, 
  s.Address,
  p.Price1,
  p.Price2, 
  p.Price3
from

-- Compose owners list
(
  select 
  FrID, 
  (
    SELECT FirstName + ' ' + LastName + ';'
    FROM franchise as inner1
    WHERE inner1.FrID = outer1.FrID
    FOR XML PATH('')
  ) as OwnersList
  from franchise as outer1
  group by FrID
) as f (FrId, OwnersList)
join store s on f.FrID = s.FrID 
join pricing p on p.StoreNumber = s.StoreNumber 

以下是第二个查询的输出。

enter image description here