递归sql问题

时间:2008-12-18 14:42:21

标签: sql

我有一个问题,我希望通过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不是自己的有效产品报价)。

先决条件: 发送的产品实例组合只能产生一个特定的 产品组合的组合,因此每个查询只有一个解决方案。

11 个答案:

答案 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)
      )
  );

这只会产生填充给定集合的结果,这些结果与所有其他结果不相交,我认为就是你所要求的。对于给出的测试示例:

  • 提供10,11,12产生A
  • 提供10,11,13,得到B,C

答案 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中有这样的东西,它允许你加入一个查询与它自己的结果,直到那里不再返回任何行)可以通过将上一步的结果与所有产品交叉加入然后过滤掉无效组合来“收集”正确的答案。然而,您将获得有效组合的所有排列,并且它几乎不会有效。这个想法很模糊,所以我不能保证它实际上可以实现。