有没有办法在 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