如何编写将数据扩展为多行的数据库视图?

时间:2011-08-22 03:01:59

标签: sql oracle

我有一个数据库表,其中包含从供应商处收集的产品的收集数据,我需要使用Oracle SQL查询生成该供应商的月初至今生产数据的估算值。每天可以有多个集合,每个集合可以包含多天生成的产品。

以下是原始收集数据的示例:

Date        Volume  ColectionNumber  ProductionDays
2011-08-22  500     1                2
2011-08-22  200     2                2
2011-08-20  600     1                2

创建一个月初至今的估算是棘手的,因为该月的第一天可能有两天的生产收集。该收集量中只有一部分实际上归因于当月。

如何编写查询以产生此估算值?

我的直觉是,我应该能够创建一个数据库视图,通过在同一天汇总集合并在整个产品生成天数内分配集合量,将原始数据转换为估计的每日生产数据。这将允许我编写一个简单的查询来查找月初至今的生产数据。

以下是上述收集数据在转换为估计的每日生产数据后的样子:

Date        VolumeEstimate
2011-08-22  350
2011-08-21  350
2011-08-20  300
2011-08-19  300

我是否在正确的轨道上?如果是这样,如何实施?我完全不知道如何在SQL中进行这种类型的转换。如果没有,那么更好的方法是什么?

注意:我无法在应用程序代码中进行此计算,因为这将需要我们无法承受的重大代码更改。

3 个答案:

答案 0 :(得分:1)

CREATE TABLE TableA (ProdDate DATE, Volume NUMBER, CollectionNumber NUMBER, ProductionDays NUMBER);
INSERT INTO TableA VALUES (TO_DATE ('20110822', 'YYYYMMDD'), 500, 1, 2);
INSERT INTO TableA VALUES (TO_DATE ('20110822', 'YYYYMMDD'), 200, 2, 2);
INSERT INTO TableA VALUES (TO_DATE ('20110820', 'YYYYMMDD'), 600, 1, 2);
COMMIT;

CREATE VIEW DailyProdVolEst AS
SELECT DateList.TheDate, SUM (DateRangeSums.DailySum) VolumeEstimate FROM
(
SELECT ProdStart, ProdEnd, SUM (DailyProduction) DailySum
FROM
(
SELECT (ProdDate - ProductionDays + 1) ProdStart, ProdDate ProdEnd, CollectionNumber, VolumeSum/ProductionDays DailyProduction
FROM
(
Select ProdDate, CollectionNumber, ProductionDays, Sum (Volume) VolumeSum FROM TableA
GROUP BY ProdDate, CollectionNumber, ProductionDays
)
)
GROUP BY ProdStart, ProdEnd
) DateRangeSums,
(
SELECT A.MinD + MyList.L TheDate FROM
(SELECT  MIN (ProdDate - ProductionDays + 1) MinD FROM TableA) A,
(SELECT LEVEL - 1 L FROM DUAL CONNECT BY LEVEL <= (SELECT  Max (ProdDate) - MIN (ProdDate - ProductionDays + 1) + 1 FROM TableA)) MyList
) DateList
WHERE DateList.TheDate BETWEEN DateRangeSums.ProdStart AND DateRangeSums.ProdEnd
GROUP BY DateList.TheDate;

视图DailyProdVolEst动态地为您提供了您描述的结果......虽然适用了一些“约束”:

  • ProdDateCollectionNumber的组合应该是唯一的。
  • ProductionDays需要&gt;所有行都为0

编辑 - 根据要求提供的评论:

此查询的工作原理:
它找出表中最小的+最大日期是什么,然后构建行,每行是该范围内的日期(DateList)...这与包含每日总和的行列表相匹配ProdDate开始/结束(DateRangeSums)的独特组合,并在日期级别汇总。

SUM (DateRangeSums.DailySum)SUM (DailyProduction)做了什么?

两者总结 - SUM (DateRangeSums.DailySum)总结了部分重叠日期范围的情况,SUM (DailyProduction)总结了一个日期范围内如果有多个CollectionNumber。如果没有SUM,则不需要GROUP BY

答案 1 :(得分:0)

我认为UNION查询可以帮到你。您没有在示例中使用CollectionNumber字段,因此我将其从下面的示例中排除。

类似于以下查询的东西应该有效(免责声明:目前我无法访问我的oracle db):

SELECT Date, SUM(Volume) VolumeEstimate
FROM
   (SELECT Date, SUM(Volume / ProductionDays) Volume
    FROM [Table]
    GROUP BY Date

    UNION

    SELECT (Date - 1) Date, SUM(Volume / 2)
    WHERE ProductionDays = 2
    GROUP BY Date - 1)
GROUP BY Date

答案 2 :(得分:0)

听起来你想做的就是白天总结,然后使用计数表来划分结果。

这是一个runnable example,其中包含T-SQL方言中的数据:

DECLARE @tbl AS TABLE (
  [Date] DATE
  , Volume INT
  , ColectionNumber INT
  , ProductionDays INT);
INSERT INTO @tbl
  VALUES ('2011-08-22', 500, 1, 2)
  , ('2011-08-22', 200, 2, 2)
  , ('2011-08-20', 600, 1, 2);

WITH Numbers AS (SELECT 1 AS N UNION ALL SELECT 2 AS N)
,AssignedVolumes AS (
    SELECT t.*
        , t.Volume / t.ProductionDays AS PerDay
        , DATEADD(d, 1 - n.N, t.[Date]) AS AssignedDate
    FROM @tbl AS t
    INNER JOIN Numbers AS n
        ON n.N <= t.ProductionDays
)
SELECT AssignedDate
    , SUM(PerDay)
FROM AssignedVolumes
GROUP BY AssignedDate;​

我打了一个简单的数字表,其中只有1和2来执行枢轴。通常,您将拥有一个包含数百万个数字的表格。

对于Oracle,您唯一需要更改的是DATEADD。