SQL - 查找顶级父级和底级子级值

时间:2016-11-08 14:36:26

标签: sql-server tsql

我有一个难题。情况是我们有一个表(OSCL)。此表有一个子表(SCL4),它链接到更多表(ODLN - > DLN1)。

这里的技巧是,OSCL有一个递归链接回自身的条目(“u_callParent”字段中将包含一个与之对应的“CallID”。)

通常情况下,需要在查询中链接回0-3级别的“呼叫父母”。然而,一旦在蓝色的月亮中,父母召唤可能会有多达15级。

此查询的最终目标是数据集如下:

DLN1.DocEntry |  ODLN.DocNum | DLN1.ItemCode | DLN1.StockPrice | 
DLN1.Quantity | DLN1.LineNum | most parental call id | most child close date

以下是我正在处理的数据示例:

OSCL
--------------------------------------
CallID  | u_callParent | CloseDate   |
--------|-----------------------------
11638   | null         | 7/1/2016    |
11688   | 11638        | 7/3/2016    |
12548   | 11688        | 7/7/2016    |
12705   | 12548        | 7/8/2016    |
12845   | 12705        | 7/15/2016   |
13321   | 12845        | 7/18/2016   |
13643   | 13321        | 7/21/2016   | 
13661   | 13643        | 7/24/2016   |
13872   | 13661        | 7/29/2016   |
--------------------------------------

   ^
   |
   v

SCL4
------------------------
SrcvCallID  | DocAbs   |
------------|-----------
11638       | 7541     |
11688       | null     |
12548       | null     |
12705       | null     |
12845       | 8993     |
13321       | 9305     |
13643       | 9335     |
13661       | 9408     |
13872       | 10519    |
------------------------

                ^
               /
              /
             /
            /
           /
          v
ODLN
------------------------
DocEntry    | DocNum   |
------------|-----------
7541        | 9540     |
8993        | 10992    |
9305        | 11304    |
9335        | 11334    |
9408        | 11407    |
10519       | 12518    |
------------------------

   ^
   |
   v

DLN1
----------------------------------------------------
DocEntry    | ItemCode       | Quantity | LineNum  |
------------|---------------------------------------
7541        | 6LH06990000    | 1        | 0        |
7541        | 6LE49877000    | 1        | 1        |
8993        | 6LE09191000    | 1        | 0        |
8993        | 6LE09788000    | 1        | 1        |
8993        | 6LE09132000    | 1        | 2        |
8993        | 6LE09155000    | 1        | 3        |
8993        | C010814000     | 1        | 4        |
8993        | 6LH72649000    | 1        | 5        |
9305        | LaborDefault   | 2.113    | 0        |
9335        | LaborDefault   | 1        | 0        |
9408        | LaborDefault   | 1.131    | 0        |
10519       | LaborDefault   | 3.213    | 0        |
10519       | 6LA15184000    | 3        | 1        |
10519       | 6LE09604000    | 1        | 2        |
----------------------------------------------------

Goal Results
------------
DocEntry | DocNum | ItemCode  | Quantity | LineNum | CallID | CloseDate | 
-------------------------------------------------------------------------
7541     | 9540   | 6LH...    | 1        | 0       | 11638  | 7/29/2016 |
7541     | 9540   | 6LE4...   | 1        | 1       | 11638  | 7/29/2016 |
8993     | 10992  | 6LE0...   | 1        | 0       | 11638  | 7/29/2016 |
8993     | 10992  | etc...    | 1        | 1       | 11638  | 7/29/2016 |
8993     | 10992  |           | 1        | 2       | 11638  | 7/29/2016 |
8993     | 10992  |           | 1        | 3       | 11638  | 7/29/2016 |
8993     | 10992  |           | 1        | 4       | 11638  | 7/29/2016 |
8993     | 10992  |           | 1        | 5       | 11638  | 7/29/2016 |
9305     | 11304  |           | 2.113    | 0       | 11638  | 7/29/2016 |
9335     | 11334  |           | 1        | 0       | 11638  | 7/29/2016 |
9408     | 11407  |           | 1.131    | 0       | 11638  | 7/29/2016 |
10519    | 12518  |           | 3.213    | 0       | 11638  | 7/29/2016 |
10519    | 12518  |           | 3        | 1       | 11638  | 7/29/2016 |
10519    | 12518  |           | 1        | 2       | 11638  | 7/29/2016 |
-------------------------------------------------------------------------

箭头表示表格之间的链接。

我编写了以下查询,返回了正确的结果;然而,仅运行1:57只能运行23行(限制器来自始发呼叫11638)。问题是我需要在where语句中使用日期范围参数运行它,并且我们不希望每次重新运行此查询时等待2分钟以上。

select s1.*
, max(isnull(isnull(isnull(isnull(isnull(isnull(isnull(isnull(isnull(isnull(isnull(isnull(isnull(isnull(isnull(child15.callid, child14.callid),child13.callid),child12.callid),child11.callid),child10.callid),child9.callid),child8.callid),child7.callid),child6.callid),child5.callid),child4.callid),child3.callid),child2.callid),child1.callid),oscl.callID)) MinCallID

 FROM (
select odln.docentry
, odln.docnum
, dln1.itemcode
, dln1.Quantity
, dln1.LineNum
, min(isnull(isnull(isnull(isnull(isnull(isnull(isnull(isnull(isnull(isnull(isnull(isnull(isnull(isnull(isnull(parent15.callid, parent14.callid),parent13.callid),parent12.callid),parent11.callid),parent10.callid),parent9.callid),parent8.callid),parent7.callid),parent6.callid),parent5.callid),parent4.callid),parent3.callid),parent2.callid),parent1.callid),oscl.callID)) MinCallID

 from SCL4 
inner join odln on odln.DocEntry = scl4.DocAbs
left join dln1 on dln1.DocEntry = odln.DocEntry
left join oscl on oscl.callid = scl4.SrcvCallID
left join oscl parent1 on oscl.U_callParent = parent1.callID
left join oscl parent2 on parent1.U_callParent = parent2.callID
left join oscl parent3 on parent2.U_callParent = parent3.callID
left join oscl parent4 on parent3.U_callParent = parent4.callID
left join oscl parent5 on parent4.U_callParent = parent5.callID
left join oscl parent6 on parent5.U_callParent = parent6.callID
left join oscl parent7 on parent6.U_callParent = parent7.callID
left join oscl parent8 on parent7.U_callParent = parent8.callID
left join oscl parent9 on parent8.U_callParent = parent9.callID
left join oscl parent10 on parent9.U_callParent = parent10.callID
left join oscl parent11 on parent10.U_callParent = parent11.callID
left join oscl parent12 on parent11.U_callParent = parent12.callID
left join oscl parent13 on parent12.U_callParent = parent13.callid  
left join oscl parent14 on parent13.U_callParent = parent14.callID
left join oscl parent15 on parent14.U_callParent = parent15.callID



group by odln.docentry
, odln.docnum
, dln1.itemcode
, dln1.Quantity
, dln1.LineNum

) s1
left join oscl on oscl.callid = s1.MinCallID
left join oscl child1 on child1.U_callParent = oscl.callID
left join oscl child2 on child2.U_callParent = child1.callID
left join oscl child3 on child3.U_callParent = child2.callID
left join oscl child4 on child4.U_callParent = child3.callID
left join oscl child5 on child5.U_callParent = child4.callID
left join oscl child6 on child6.U_callParent = child5.callID
left join oscl child7 on child7.U_callParent = child6.callID
left join oscl child8 on child8.U_callParent = child7.callID
left join oscl child9 on child9.U_callParent = child8.callID
left join oscl child10 on child10.U_callParent = child9.callID
left join oscl child11 on child11.U_callParent = child10.callID
left join oscl child12 on child12.U_callParent = child11.callID
left join oscl child13 on child13.U_callParent = child12.callid 
left join oscl child14 on child14.U_callParent = child13.callID
left join oscl child15 on child15.U_callParent = child14.callID

--where MinCallID = 11638

group by s1.docentry
, s1.docnum
, s1.itemcode
, s1.stockprice
, s1.Quantity
, s1.LineNum
, s1.MinCallID

order by s1.mincallid, docentry, LineNum

我知道这有点吝啬,而且必须有更好的方法来写它,但对于我的生活,我无法理解。

我也无法对数据库表添加任何修改(因此我无法添加称为“始发呼叫”或其他任何内容的字段)。

对于它的价值,这些表都基于SAP Business One,在OSCL上使用UDF for u_callParent。

编辑:

根据以下评论,我已将查询更新为:

with cte as (

                SELECT
                    OSCL.CallID
                    , OSCL.U_callParent
                    , PLevel = 0
                    , TopParent = CallID
                FROM OSCL
                WHERE U_callParent is null

                UNION ALL

                SELECT 
                    OSCL.CallID
                    , OSCL.U_callParent
                    , PLevel = cte.Plevel + 1
                    , cte.TopParent
                FROM OSCL
                INNER JOIN cte on oscl.U_callParent = cte.callID
)

SELECT 
cte.* , scl4.DocAbs, scl4.Object
FROM cte
left join scl4 on scl4.SrcvCallID = cte.callID
where TopParent = 11638

现在的问题是我需要获取OSCL.CloseDate以获取“最幼稚”的信息,而且我不确定这样做的好方法。有什么想法吗?

编辑2:

好吧,看起来我想通了:

with cte as (

                SELECT
                    OSCL.CallID
                    , OSCL.U_callParent
                    , PLevel = 0
                    , TopParent = CallID
                FROM OSCL
                WHERE U_callParent is null

                UNION ALL

                SELECT 
                    OSCL.CallID
                    , OSCL.U_callParent
                    , PLevel = cte.Plevel + 1
                    , cte.TopParent
                FROM OSCL
                INNER JOIN cte on oscl.U_callParent = cte.callID
)

SELECT 
odln.docentry, odln.docnum, dln1.itemcode, dln1.quantity, dln1.linenum, cte.TopParent, child.closeDate
FROM cte
left join scl4 on scl4.SrcvCallID = cte.callID
left join (SELECT max(callid) callID, topParent from cte group by topParent) s1 on s1.TopParent = cte.TopParent
inner join oscl Child on Child.callID = s1.callID
left join odln on odln.ObjType = scl4.Object and odln.DocEntry = scl4.DocAbs
left join dln1 on dln1.DocEntry = odln.DocEntry
where cte.TopParent = 11638

如果在我上一个select语句中使用子查询的方法比在上一个select语句中使用更好,请告诉我,但这似乎很快就会运行。

1 个答案:

答案 0 :(得分:1)

with cte as (

            SELECT
                OSCL.CallID
                , OSCL.U_callParent
                , PLevel = 0
                , TopParent = CallID
            FROM OSCL
            WHERE U_callParent is null

            UNION ALL

            SELECT 
                OSCL.CallID
                , OSCL.U_callParent
                , PLevel = cte.Plevel + 1
                , cte.TopParent
            FROM OSCL
            INNER JOIN cte on oscl.U_callParent = cte.callID
)

SELECT 
odln.docentry, odln.docnum, dln1.itemcode, dln1.quantity, dln1.linenum, cte.TopParent, child.closeDate
FROM cte
left join scl4 on scl4.SrcvCallID = cte.callID
left join (SELECT max(callid) callID, topParent from cte group by topParent) s1 on s1.TopParent = cte.TopParent
inner join oscl Child on Child.callID = s1.callID
left join odln on odln.ObjType = scl4.Object and odln.DocEntry = scl4.DocAbs
left join dln1 on dln1.DocEntry = odln.DocEntry
where cte.TopParent = 11638

这是我发现最终工作的解决方案。