简体   繁体   English

如何在SQL查询中填充Min和Max之间所有值的范围?

[英]How to fill a range of all values between Min and Max in SQL query?

I have the following view: 我有以下观点:

select v.* from myview v;

PeriodID | StartMonth | EndMonth
102      |          1 |        4
103      |          3 |        7
104      |          4 |        11

And I need to mix this view with this query (another view). 我需要将此视图与此查询(另一个视图)混合使用。

select q.* from query q;

Somevalue| AnotherValue| PeriodId
   'abc' |         546 |      102
   'xyz' |         147 |      103
   'bnm' |         652 |      104

I need: 我需要:

Somevalue| AnotherValue| PeriodId | Month
   'abc' |         546 |      102 |     1
   'abc' |         546 |      102 |     2
   'abc' |         546 |      102 |     3
   'abc' |         546 |      102 |     4
   'xyz' |         147 |      103 |     3
   'xyz' |         147 |      103 |     4
   'xyz' |         147 |      103 |     5
   'xyz' |         147 |      103 |     6
   'xyz' |         147 |      103 |     7

If I can get a collection from startmonth to endmonth and join it with q, maybe I could get what I need. 如果我可以从startmonth to endmonth获得一个集合,并与q startmonth to endmonth加入,也许我可以得到我所需要的。

from: 从:

PeriodID | StartMonth | EndMonth
102      |          1 |        4
103      |          3 |        7
104      |          4 |        11

to: 至:

PeriodID |      Month
102      |          1
102      |          2
102      |          3
102      |          4

I changed names to make query clearer 我更改了名称以使查询更清晰

myview --> periods_table

query --> values_table

SELECT vl.*
      ,pr.month
  FROM (SELECT periodid AS periodid 
              ,LEVEL    AS month
          FROM periods_table t
         WHERE LEVEL >= t.startmonth
         GROUP BY periodid, LEVEL
        CONNECT BY LEVEL <= t.endmonth) pr
      , values_table vl
 WHERE pr.periodid = vl.periodid
 ORDER BY pr.periodid, pr.month
with t as (select 1 monthid from dual union all select 2 from dual
           ...)
select q.somevalue, q.anothervalue, m.periodid, t.monthid
from t 
join myview m on t.monthid between m.startmonth and m.endmonth
join q on q.periodid = m.periodid

You can construct a months table with numbers and join it to your table. 您可以用数字构造一个月表并将其join到表中。

Two solutions to give the same result: 给出相同结果的两种解决方案:

SQL Fiddle SQL小提琴

Oracle 11g R2 Schema Setup : Oracle 11g R2架构设置

CREATE TABLE myview ( PeriodID, StartMonth, EndMonth ) AS
          SELECT 102, 1, 4 FROM DUAL
UNION ALL SELECT 103, 3, 7 FROM DUAL
UNION ALL SELECT 104, 4, 11 FROM DUAL;

CREATE TABLE query ( Somevalue, AnotherValue, PeriodId ) AS
          SELECT 'abc', 546, 102 FROM DUAL
UNION ALL SELECT 'xyz', 147, 103 FROM DUAL
UNION ALL SELECT 'bnm', 652, 104 FROM DUAL;

Query 1 - Using Recursive Subquery Factoring : 查询1-使用递归子查询分解

WITH Periods ( PeriodID, Month, EndMonth ) AS (
  SELECT PeriodID, StartMonth, EndMonth
  FROM   myview
  WHERE  StartMonth <= EndMonth
  UNION ALL
  SELECT PeriodID, Month + 1, EndMonth
  FROM   Periods
  WHERE  Month < EndMonth
)
SELECT p.PeriodID,
       q.SomeValue,
       q.AnotherValue,
       p.Month
FROM   Periods p
       INNER JOIN
       Query q
       ON ( p.PeriodID = q.PeriodID )
ORDER BY PeriodID, Month

Query 2 - Using a Hierarchical Query : 查询2-使用分层查询

WITH Periods ( PeriodID, Month ) AS (
  SELECT m.PeriodID,
         t.COLUMN_VALUE
  FROM   myview m,
         TABLE(
           CAST(
             MULTISET(
               SELECT m.StartMonth + LEVEL - 1
               FROM   DUAL
               CONNECT BY m.StartMonth + LEVEL - 1 <= m.EndMonth
             )
             AS SYS.ODCINUMBERLIST
           )
         ) t
)
SELECT p.PeriodID,
       q.SomeValue,
       q.AnotherValue,
       p.Month
FROM   Periods p
       INNER JOIN
       Query q
       ON ( p.PeriodID = q.PeriodID )

Results : 结果

| PERIODID | SOMEVALUE | ANOTHERVALUE | MONTH |
|----------|-----------|--------------|-------|
|      102 |       abc |          546 |     1 |
|      102 |       abc |          546 |     2 |
|      102 |       abc |          546 |     3 |
|      102 |       abc |          546 |     4 |
|      103 |       xyz |          147 |     3 |
|      103 |       xyz |          147 |     4 |
|      103 |       xyz |          147 |     5 |
|      103 |       xyz |          147 |     6 |
|      103 |       xyz |          147 |     7 |
|      104 |       bnm |          652 |     4 |
|      104 |       bnm |          652 |     5 |
|      104 |       bnm |          652 |     6 |
|      104 |       bnm |          652 |     7 |
|      104 |       bnm |          652 |     8 |
|      104 |       bnm |          652 |     9 |
|      104 |       bnm |          652 |    10 |
|      104 |       bnm |          652 |    11 |

Here is the answer of your question. 这是您问题的答案。

DECLARE @TABLE1 TABLE
(
  PERIODID INT,
  STARTMONTH INT,
  ENDMONTH INT
)

DECLARE @TABLE2 TABLE
(
  SOMEVALUE VARCHAR(20),
  ANOTHERVALUE INT,
  PERIODID INT
)

INSERT INTO @TABLE1 VALUES (102,1,3)
INSERT INTO @TABLE1 VALUES (103,3,7)
INSERT INTO @TABLE2 VALUES ('ABC',123,102)
INSERT INTO @TABLE2 VALUES ('BCD',234,103)

;WITH cte
 AS (SELECT PERIODID, STARTMONTH [MONTH],ENDMONTH
     FROM   @TABLE1 
     UNION ALL
     SELECT PERIODID, [MONTH] + 1,ENDMONTH
     FROM   cte
     WHERE  [MONTH] < ENDMONTH)

SELECT T2.SOMEVALUE,T2.ANOTHERVALUE, CTE.PERIODID,CTE.[MONTH] FROM CTE 
INNER JOIN @TABLE2 T2 ON CTE.PERIODID =  T2.PERIODID
ORDER BY CTE.PERIODID,CTE.MONTH

Check this SQLFiddle out and try to fit according to your scenario. 检查此SQLFiddle并尝试根据您的情况进行调整。 It will surely works. 它肯定会工作。

Happy Coding 快乐编码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM