我有一个问题,我希望通过SQL查询解决。这是 被用作PoC(概念证明)。
问题:
产品由一个或多个产品实例组成,即产品 实例可以属于许多产品。 这可以在表格中实现:
PO | PI
-----
A | 10
A | 11
A | 12
B | 10
B | 11
C | 13
现在我想从一组产品实例中获取产品报价。 例如。如果我们发送10,11,13,预期结果是B& C,如果我们发送 只有10,那么结果应该是NULL,因为没有产品提供 只有10.在10,11,12中发送将导致A(不是A& B,因为12不是自己的有效产品报价)。
先决条件: 发送的产品实例组合只能产生一个特定的 产品组合的组合,因此每个查询只有一个解决方案。
答案 0 :(得分:7)
好的,我想我拥有它。这符合您提供的限制。可能有一种方法可以进一步简化这一点,但它让我的大脑稍微吃了一点:
select distinct PO
from POPI x
where
PO not in (
select PO
from POPI
where PI not in (10,11,12)
)
and PI not in (
select PI
from POPI
where PO != x.PO
and PO not in (
select PO
from POPI
where PI not in (10,11,12)
)
);
这只会产生填充给定集合的结果,这些结果与所有其他结果不相交,我认为就是你所要求的。对于给出的测试示例:
答案 1 :(得分:2)
编辑:虽然我认为我的工作正常,但亚当的答案毫无疑问会更优雅,更有效率 - 我会把我的遗留给后人!
道歉,因为我知道自从我开始播放以来,这已被标记为Oracle问题。这是一些SQL2008代码,我认为它适用于所有陈述的情况......
declare @test table
(
[PI] int
)
insert @test values (10), (11), (13)
declare @testCount int
select @testCount = COUNT(*) from @test
;with PO_WITH_COUNTS as
(
select PO_FULL.PO, COUNT(PO_FULL.[PI]) PI_Count
from ProductOffering PO_FULL
left
join (
select PO_QUALIFYING.PO, PO_QUALIFYING.[PI]
from ProductOffering PO_QUALIFYING
where PO_QUALIFYING.[PI] in (select [PI] from @test)
) AS QUALIFYING
on QUALIFYING.PO = PO_FULL.PO
and QUALIFYING.[PI] = PO_FULL.[PI]
group by
PO_FULL.PO
having COUNT(PO_FULL.[PI]) = COUNT(QUALIFYING.[PI])
)
select PO_OUTER.PO
from PO_WITH_COUNTS PO_OUTER
cross
join PO_WITH_COUNTS PO_INNER
where PO_OUTER.PI_Count = @testCount
or PO_OUTER.PO <> PO_INNER.PO
group by
PO_OUTER.PO, PO_OUTER.PI_Count
having PO_OUTER.PI_Count = @testCount
or PO_OUTER.PI_Count + SUM(PO_INNER.PI_Count) = @testCount
不确定Oracle是否有CTE,但可以将内部查询说明为两个派生表。外部查询中的交叉连接让我们可以找到包含所有有效项的产品组合。我知道这只会基于问题中的声明,即数据是每个请求的集合只有1个有效组合,如果不是这样,它会更复杂,因为计数不足以删除具有重复产品的组合它们。
答案 2 :(得分:1)
我没有在我面前有一个数据库,但是在我的头脑中你想要的输入列表中没有任何PI的PO列表,即
select distinct po
from tbl
where po not in ( select po from tbl where pi not in (10,11,13) )
编辑:以下是其他案例:
当输入PI = 10,11,13时,内部选择返回A,因此外部选择返回B,C
当输入PI = 10时,内部选择返回A,B,C,因此外部选择不返回行
当输入PI = 10,11,12时,内部选择返回C,因此外部选择返回A,B
编辑:亚当已经指出,这最后一个案例不符合只返回A的要求(这将教我冲过去),所以这还没有工作代码。
答案 3 :(得分:1)
Select Distinct PO
From Table T
-- Next eliminates POs that contain other PIs
Where Not Exists
(Select * From Table
Where PO = T.PO
And PI Not In (10, 11, 12))
-- And this eliminates POs that do not contain all the PIs
And Not Exists
(Select Distinct PI From Table
Where PI In (10, 11, 12)
Except
Select Distinct PI From Table
Where PO = T.PO
或者,如果您的数据库没有实现EXCEPT ...
Select Distinct PO
From Table T
-- Next predicate eliminates POs that contain other PIs
Where Not Exists
(Select * From Table
Where PO = T.PO
And PI Not In (10, 11, 12))
-- And this eliminates POs that do not contain ALL the PIs
And Not Exists
(Select Distinct PI From Table A
Where PI In (10, 11, 12)
And Not Exists
(Select Distinct PI From Table
Where PO = T.PO
And PdI = A.PI))
答案 4 :(得分:1)
客户是否可能不止一次要求提供产品?
例如:他/她要求提供10,10,11,11,12?
如果这可能比
这样的解决方案选择...... 来自...... pi in(10,10,11,11,12)
不起作用。
因为'pi in(10,10,11,11,12)'与'pi in(10,11,12)'相同。
10,10,11,11,12的解决方案是A&amp; B。
答案 5 :(得分:0)
这里有一些伪代码:
从表中选择PI = 10或pi = 11等
将结果存储在临时表
中从临时表中选择不同的PO和计数(PI)。
现在,对于每个PO,您都可以获得可用的PI产品总数。如果可用的PI数量与临时表中的计数匹配,则表示您拥有该PO的所有PI。添加所有PO,然后添加结果集。
答案 6 :(得分:0)
您需要计算列表中的项目,即@list_count。找出哪些产品具有不在列表中的实例。选择 列表中 列出实例的所有产品:
select P0,count(*) c from table where P0 not in (
select P0 from table where P1 not in (@list)
) and P1 in (@list) group by P0
我会将其存储在临时表中并选择*记录,其中c = @list_count
答案 7 :(得分:0)
如果我们重新定义一个问题:
让我们有一个包含产品实例的客户表:
crete table cust_pi (
pi varchar(5),
customer varchar(5));
和“product_catalogue”表:
CREATE TABLE PI_PO_TEST
("PO" VARCHAR2(5 CHAR),
"PI" VARCHAR2(5 CHAR)
);
让我们填写一些样本数据:
insert into CUST_PI (PI, CUSTOMER)
values ('11', '1');
insert into CUST_PI (PI, CUSTOMER)
values ('10', '1');
insert into CUST_PI (PI, CUSTOMER)
values ('12', '1');
insert into CUST_PI (PI, CUSTOMER)
values ('13', '1');
insert into CUST_PI (PI, CUSTOMER)
values ('14', '1');
insert into PI_PO_TEST (PO, PI)
values ('A', '10');
insert into PI_PO_TEST (PO, PI)
values ('A', '11');
insert into PI_PO_TEST (PO, PI)
values ('A', '12');
insert into PI_PO_TEST (PO, PI)
values ('A', '13');
insert into PI_PO_TEST (PO, PI)
values ('B', '14');
insert into PI_PO_TEST (PO, PI)
values ('C', '11');
insert into PI_PO_TEST (PO, PI)
values ('C', '12');
insert into PI_PO_TEST (PO, PI)
values ('D', '15');
insert into PI_PO_TEST (PO, PI)
values ('D', '14');
然后我的第一个拍摄解决方案是这样的:
select po1 po /* select all product offerings that match the product definition
(i.e. have the same number of product instances per offering as
in product catalogue */
from (select po po1, count(c.pi) k1
from cust_pi c, pi_po_test t
where c.pi = t.pi
and customer = 1
group by po) t1,
(select po po2, count(*) k2 from pi_po_test group by po) t2
where k1 = k2
and po1 = po2
minus /* add those, that are contained within others */
select slave
from (select po2 master, po1 slave
/* this query returns, that if you have po "master" slave should be removed from result,
as it is contained within*/
from (select t1.po po1, t2.po po2, count(t1.po) k1
from pi_po_test t1, pi_po_test t2
where t1.pi = t2.pi
group by t1.po, t2.po) t1,
(select po, count(po) k2 from pi_po_test group by po) t2
where t1.po2 = t2.po
and k1 < k2)
where master in
/* repeated query from begining. This could be done better :-) */
(select po1 po
from (select po po1, count(c.pi) k1
from cust_pi c, pi_po_test t
where c.pi = t.pi
and customer = 1
group by po) t1,
(select po po2, count(*) k2 from pi_po_test group by po) t2
where k1 = k2
and po1 = po2)
所有这些都是在Oracle上完成的,因此您的里程可能会有所不同
答案 8 :(得分:0)
我在4组值下测试了它们,它们都返回了正确的结果。这使用我在SQL中使用的函数来从由分号分隔的参数字符串生成表。
DECLARE @tbl TABLE (
po varchar(10),
pii int)
INSERT INTO @tbl
SELECT 'A', 10
UNION ALL
SELECT 'A', 11
UNION ALL
SELECT 'A', 12
UNION ALL
SELECT 'B', 10
UNION ALL
SELECT 'B', 11
UNION ALL
SELECT 'C', 13
DECLARE @value varchar(100)
SET @value = '10;11;12;'
--SET @value = '11;10;'
--SET @value = '13;'
--SET @value = '10;'
SELECT DISTINCT po
FROM @tbl a
INNER JOIN fMultiValParam (@value) p ON
a.pii = p.paramid
WHERE a.po NOT IN (
SELECT t.po
FROM @tbl t
LEFT OUTER JOIN (SELECT *
FROM @tbl tt
INNER JOIN fMultiValParam (@value) p ON
tt.pii = p.paramid) tt ON
t.pii = tt.pii
AND t.po = tt.po
WHERE tt.po IS NULL)
这是函数
CREATE FUNCTION [dbo].[fMultiValParam]
(@Param varchar(5000))
RETURNS @tblParam TABLE (ParamID varchar(40))
AS
BEGIN
IF (@Param IS NULL OR LEN(@Param) < 2)
BEGIN
RETURN
END
DECLARE @len INT
DECLARE @index INT
DECLARE @nextindex INT
SET @len = DATALENGTH(@Param)
SET @index = 0
SET @nextindex = 0
WHILE (@index < @len)
BEGIN
SET @Nextindex = CHARINDEX(';', @Param, @index)
INSERT INTO @tblParam
SELECT SUBSTRING(@Param, @index, @nextindex - @index)
SET @index = @nextindex + 1
END
RETURN
END
答案 9 :(得分:0)
试试这个:
SELECT DISTINCT COALESCE ( offer, NULL )
FROM products
WHERE instance IN ( @instancelist )
答案 10 :(得分:-1)
在没有一些存储过程代码的情况下,通过纯SQL无法进行IMHO。但是......我不确定。
添加:另一方面,我对递归查询有所了解(在MSSQL 2005中有这样的东西,它允许你加入一个查询与它自己的结果,直到那里不再返回任何行)可以通过将上一步的结果与所有产品交叉加入然后过滤掉无效组合来“收集”正确的答案。然而,您将获得有效组合的所有排列,并且它几乎不会有效。这个想法很模糊,所以我不能保证它实际上可以实现。