Sql 如何添加多列值?

Sql 如何添加多列值?,sql,oracle,Sql,Oracle,我必须询问那些在2010年1月至2012年12月期间行驶超过43万英里的司机。 我正为此努力 SELECT t.DRIVERID FROM BBI_BC.TRUCKS t WHERE t.JAN_2010_MILES + t. FEB_2010_MILES + .....+ t.DEC_2012_MILES > 43000; 所以问题是,如何避免在WHERE子句中键入所有这些列。答案是你不能。该表的设计者选择忽略已确立的数据库规范化行业标准规则。他们这样做的原因无关紧要:结果是每个查询

我必须询问那些在2010年1月至2012年12月期间行驶超过43万英里的司机。 我正为此努力

SELECT t.DRIVERID 
FROM BBI_BC.TRUCKS t
WHERE t.JAN_2010_MILES + t. FEB_2010_MILES + .....+ t.DEC_2012_MILES > 43000;

所以问题是,如何避免在WHERE子句中键入所有这些列。答案是你不能。该表的设计者选择忽略已确立的数据库规范化行业标准规则。他们这样做的原因无关紧要:结果是每个查询表的人都必须编写冗长且难以维护的查询

可以选择动态查询生成:运行此查询,然后剪切“n”并将输出粘贴到WHERE子句中:

select ' + t.' || column_name
from all_tab_columns
where owner = 'BBI_BC'
and table_name = 'TRUCKS'
and (column_name like '%2010_MILES'
     or column_name like '%2011_MILES'
     or column_name like '%2012_MILES')
order by column_id;
我怀疑这是一个家庭作业问题。重要的是你要意识到真正的教训是:数据建模非常重要,非规范化非常糟糕

甚至表的列的命名方式也很糟糕:如果将它们命名为MILES\u 2010\u 01、MILES\u 2010\u 02等会更好,因为至少这样您可以将动态查询编写为

where column_name between 'MILES_2010_01' and 'MILES_2012_12'
使用一个正确建模的表(即每个月的记录和两列每月的英里数和每月的燃气量),查询将如下所示:

SELECT t.DRIVERID 
FROM BBI_BC.TRUCKS t
WHERE t.year between 2010 and 2012
group by t.driverid having sum(t.monthly_miles) >43000
如果值可以包含空值,则需要使用COALESCE或NVL:

如果要自动生成查询,请执行以下操作:

SELECT 'SELECT DRIVERID 
FROM   BBI_BC.TRUCKS
WHERE  ' || LISTAGG( 'COALESCE( ' || TO_CHAR( ADD_MONTHS( DATE '2010-01-01', LEVEL - 1 ), 'MON_YYYY' ) || '_MILES, 0)', '
     + ' ) WITHIN GROUP ( ORDER BY LEVEL ) || ' > 430000;' AS sql
FROM   DUAL
CONNECT BY ADD_MONTHS( DATE '2010-01-01', LEVEL - 1 ) <= DATE '2012-12-01';

这是一个类似于APC的解决方案,但会动态返回结果。为12c工作+

DECLARE
    miles_cond   VARCHAR2(20) := ' > 43000';
    v_where      VARCHAR2(1000);
    x            SYS_REFCURSOR;
BEGIN
    SELECT
        ' WHERE '
        ||
            LISTAGG(column_name,' + ') WITHIN GROUP(
                ORDER BY
                    column_name
            )
        || miles_cond
    INTO v_where
    FROM
        all_tab_columns
    WHERE
        owner = 'BBI_BC'
        AND table_name = 'TRUCKS'
        AND REGEXP_LIKE ( column_name,
                          '201[0-2]_MILES' )
    ORDER BY
        column_id;

    OPEN x FOR 'SELECT * FROM TRUCKS' || v_where;

    dbms_sql.return_result(x);
END;
/

当然,您可以添加类似MT0的逻辑来处理空值。

不要使用图片,而是添加表定义和说明。更重要的是,你的问题是什么?你想找到驱动程序,你有一个查询,你认为它返回了错误的数据还是什么?这会起作用,那么你的问题是什么?如果是如何避免键入所有这些列,答案是你不能。该表的设计者选择忽略了公认的行业标准规则。他们这样做的原因无关紧要:结果是每个查询表的人都必须编写冗长且难以维护的查询。@APC不是一种冗长的方法,我在寻找更多的通用方法来查找结果。您使用的是哪种数据库模型?您的表是否每月都会增加新的列?我建议您与架构师一起检查该模型,这有点奇怪可能您可以创建一个包含unpivot子句的视图,以便它以标准化格式显示数据,其中包含日期列,或者单独的年和月列。然后你就可以对你想要的任何时间段的里程数列求和。如何得到我所问的特定查询结果?你必须把它写出来。您所能做的就是通过查询数据字典来生成一些代码,从而为自己节省一些输入。我故意避免使用动态SQL路由,因为它不是查询。如果我正确地认为这是一个家庭作业问题,我想老师会惊讶地看到一个学生提交了这样的问题。@APC:嗯,答案已经发布了,我现在不能做太多:-。我希望将来有一天他的老板会给他分配一项任务——更复杂的SQL,我不会来这里回答。
SELECT DRIVERID 
FROM   BBI_BC.TRUCKS
WHERE  COALESCE( JAN_2010_MILES, 0)
     + COALESCE( FEB_2010_MILES, 0)
     + COALESCE( MAR_2010_MILES, 0)
     + COALESCE( APR_2010_MILES, 0)
     + COALESCE( MAY_2010_MILES, 0)
     + COALESCE( JUN_2010_MILES, 0)
     + COALESCE( JUL_2010_MILES, 0)
     + COALESCE( AUG_2010_MILES, 0)
     + COALESCE( SEP_2010_MILES, 0)
     + COALESCE( OCT_2010_MILES, 0)
     + COALESCE( NOV_2010_MILES, 0)
     + COALESCE( DEC_2010_MILES, 0)
     + COALESCE( JAN_2011_MILES, 0)
     + COALESCE( FEB_2011_MILES, 0)
     + COALESCE( MAR_2011_MILES, 0)
     + COALESCE( APR_2011_MILES, 0)
     + COALESCE( MAY_2011_MILES, 0)
     + COALESCE( JUN_2011_MILES, 0)
     + COALESCE( JUL_2011_MILES, 0)
     + COALESCE( AUG_2011_MILES, 0)
     + COALESCE( SEP_2011_MILES, 0)
     + COALESCE( OCT_2011_MILES, 0)
     + COALESCE( NOV_2011_MILES, 0)
     + COALESCE( DEC_2011_MILES, 0)
     + COALESCE( JAN_2012_MILES, 0)
     + COALESCE( FEB_2012_MILES, 0)
     + COALESCE( MAR_2012_MILES, 0)
     + COALESCE( APR_2012_MILES, 0)
     + COALESCE( MAY_2012_MILES, 0)
     + COALESCE( JUN_2012_MILES, 0)
     + COALESCE( JUL_2012_MILES, 0)
     + COALESCE( AUG_2012_MILES, 0)
     + COALESCE( SEP_2012_MILES, 0)
     + COALESCE( OCT_2012_MILES, 0)
     + COALESCE( NOV_2012_MILES, 0)
     + COALESCE( DEC_2012_MILES, 0) > 430000;
DECLARE
    miles_cond   VARCHAR2(20) := ' > 43000';
    v_where      VARCHAR2(1000);
    x            SYS_REFCURSOR;
BEGIN
    SELECT
        ' WHERE '
        ||
            LISTAGG(column_name,' + ') WITHIN GROUP(
                ORDER BY
                    column_name
            )
        || miles_cond
    INTO v_where
    FROM
        all_tab_columns
    WHERE
        owner = 'BBI_BC'
        AND table_name = 'TRUCKS'
        AND REGEXP_LIKE ( column_name,
                          '201[0-2]_MILES' )
    ORDER BY
        column_id;

    OPEN x FOR 'SELECT * FROM TRUCKS' || v_where;

    dbms_sql.return_result(x);
END;
/