【问题标题】:calculate column names automatically sql自动计算列名 sql
【发布时间】:2022-10-21 01:02:39
【问题描述】:

有没有办法在 SQL 中自动计算列名,如下所示。我需要根据从和到日期计算日历周并均匀分布

Material From To Sales
M01 03.10.2022 31.10.2022 1000
M02 14.11.2022 28.11.2022 1000

预期产出

CW =日历周

Material Cw40 CW41 Cw42 CW43 CW44 CW45 CW46 CW47
M01 250 250 250 250
M02 500 500

【问题讨论】:

  • 你的实际问题是什么?分配?动态列名?日历周的计算?

标签: sql oracle


【解决方案1】:

有没有办法在 SQL 中自动计算列名,如下所示。

不,在 SQL(不仅仅是 Oracle SQL)中,您需要固定的、已知数量的列名,因此不可能使用静态 SQL 查询动态生成列。


如果要生成数据,则可以:

将数据生成为行(而不是列)并在您用于访问数据库的任何第三方应用程序中旋转结果。您可以使用相关的行生成器生成输出:

SELECT t.material,
       w.iso_year,
       w.iso_week,
       w.weekly_sales
FROM   table_name t
       CROSS APPLY (
         SELECT TO_NUMBER(
                  TO_CHAR(
                    TRUNC(from_dt, 'IW') + INTERVAL '7' DAY * (LEVEL - 1),
                    'IYYY'
                  )
                ) AS iso_year,
                TO_NUMBER(
                  TO_CHAR(
                    TRUNC(from_dt, 'IW') + INTERVAL '7' DAY * (LEVEL - 1),
                    'IW'
                  )
                ) AS iso_week,
                ( LEAST(
                    TRUNC(from_dt, 'IW') + INTERVAL '7' DAY * LEVEL,
                    to_dt
                  )
                  - GREATEST(
                    TRUNC(from_dt, 'IW') + INTERVAL '7' DAY * (LEVEL - 1),
                    from_dt
                  )
                ) / (to_dt - from_dt) * sales AS weekly_sales
         FROM   DUAL
         CONNECT BY TRUNC(from_dt, 'IW') + INTERVAL '7' DAY * (LEVEL-1) < to_dt
       ) w

或者:

WITH data (from_dt, dt, to_dt, material, sales) AS (
  SELECT from_dt, from_dt, to_dt, material, sales
  FROM   table_name
UNION ALL
  SELECT from_dt,
         TRUNC(dt + INTERVAL '7' DAY, 'IW'),
         to_dt,
         material,
         sales
  FROM   data
  WHERE  TRUNC(dt + INTERVAL '7' DAY, 'IW') < to_dt
)
SELECT material,
       TO_NUMBER(TO_CHAR(dt, 'IYYY')) AS iso_year,
       TO_NUMBER(TO_CHAR(dt, 'IW')) AS iso_week,
       ( LEAST(dt + INTERVAL '7' DAY, to_dt) - dt)
         / (to_dt - from_dt) * sales AS weekly_sales
FROM   data

其中,对于样本数据:

CREATE TABLE table_name (Material, From_dt, To_dt, Sales) AS
SELECT 'M01', DATE '2022-10-03', DATE '2022-10-31', 1000 FROM DUAL UNION ALL
SELECT 'M02', DATE '2022-11-14', DATE '2022-11-28', 1000 FROM DUAL;

两个输出:

MATERIAL ISO_YEAR ISO_WEEK WEEKLY_SALES
M01 2022 40 250
M01 2022 41 250
M01 2022 42 250
M01 2022 43 250
M02 2022 46 500
M02 2022 47 500

或者,如果您确实想将值输出为列,那么您需要指定列(对于所有 53 个潜在的 ISO 周,这将是 53 列)并且可以使用以下方法执行此操作:

SELECT *
FROM   (
  SELECT t.material,
         w.iso_year,
         w.iso_week,
         w.weekly_sales
  FROM   table_name t
         CROSS APPLY (
           SELECT TO_NUMBER(
                    TO_CHAR(
                      TRUNC(from_dt, 'IW') + INTERVAL '7' DAY * (LEVEL - 1),
                      'IYYY'
                    )
                  ) AS iso_year,
                  TO_NUMBER(
                    TO_CHAR(
                      TRUNC(from_dt, 'IW') + INTERVAL '7' DAY * (LEVEL - 1),
                      'IW'
                    )
                  ) AS iso_week,
                  ( LEAST(
                      TRUNC(from_dt, 'IW') + INTERVAL '7' DAY * LEVEL,
                      to_dt
                    )
                    - GREATEST(
                      TRUNC(from_dt, 'IW') + INTERVAL '7' DAY * (LEVEL - 1),
                      from_dt
                    )
                  ) / (to_dt - from_dt) * sales AS weekly_sales
           FROM   DUAL
           CONNECT BY TRUNC(from_dt, 'IW') + INTERVAL '7' DAY * (LEVEL-1) < to_dt
         ) w
)
PIVOT (
  SUM(weekly_sales)
  FOR iso_week IN (
     1 AS cw01,
     2 AS cw02,
     3 AS cw03,
    -- ...
    40 AS cw40,
    41 AS cw41,
    42 AS cw42,
    43 AS cw43,
    44 AS cw44,
    45 AS cw45,
    46 AS cw46,
    47 AS cw47,
    48 AS cw48,
    49 AS cw49,
    50 AS cw50,
    51 AS cw51,
    52 AS cw52,
    53 AS cw53
  )
)

或者:

WITH data (from_dt, dt, to_dt, material, sales) AS (
  SELECT from_dt, from_dt, to_dt, material, sales
  FROM   table_name
UNION ALL
  SELECT from_dt,
         TRUNC(dt + INTERVAL '7' DAY, 'IW'),
         to_dt,
         material,
         sales
  FROM   data
  WHERE  TRUNC(dt + INTERVAL '7' DAY, 'IW') < to_dt
)
SELECT *
FROM   (
  SELECT material,
         TO_NUMBER(TO_CHAR(dt, 'IYYY')) AS iso_year,
         TO_NUMBER(TO_CHAR(dt, 'IW')) AS iso_week,
         ( LEAST(dt + INTERVAL '7' DAY, to_dt) - dt)
           / (to_dt - from_dt) * sales AS weekly_sales
  FROM   data
)
PIVOT (
  SUM(weekly_sales)
  FOR iso_week IN (
     1 AS cw01,
     2 AS cw02,
     3 AS cw03,
    -- ...
    40 AS cw40,
    41 AS cw41,
    42 AS cw42,
    43 AS cw43,
    44 AS cw44,
    45 AS cw45,
    46 AS cw46,
    47 AS cw47,
    48 AS cw48,
    49 AS cw49,
    50 AS cw50,
    51 AS cw51,
    52 AS cw52,
    53 AS cw53
  )
)

两者都输出:

MATERIAL ISO_YEAR CW01 CW02 CW03 CW40 CW41 CW42 CW43 CW44 CW45 CW46 CW47 CW48 CW49 CW50 CW51 CW52 CW53
M01 2022 null null null 250 250 250 250 null null null null null null null null null null
M02 2022 null null null null null null null null null 500 500 null null null null null null

fiddle

【讨论】:

  • 无论如何要在不使用 connect by 的情况下做到这一点?
  • @Steve您可以尝试递归子查询因式分解子句(递归WITH子句),或者将SELECT ... UNION ALL SELECT ... UNION ALL ...与52 UNION ALLs一起使用?
  • 谢谢,但是如何使用递归 WITH 子句,(至少第一部分 - 没有转置)
  • @史蒂夫更新。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-30
  • 1970-01-01
相关资源
最近更新 更多