【问题标题】:Counting the number of columns greater than 0 for each row in SQL Server 2012计算 SQL Server 2012 中每行大于 0 的列数
【发布时间】:2015-02-10 08:41:15
【问题描述】:

我有一个超过 150 列的表格。有没有办法为每个客户动态计算大于 0 的列。

表格视图就像:

     CustomerID  (SomeColumns)    Column1       Column2 ------------------ Column150
         1           -----           0            12        0  33  0  18       97
         2           -----           1             0        54  0  72  0        0
         .
         .
         .

此表有 500K 行。 column1 到 column150 的值要么为 0,要么不为 0。如何计算大于 0 的列数?

查询:

Update Table
set NumOfColumnsGreaterThanZero = (select Sum(case when Column1 to Column150 >0 then 1 else 0 end)

【问题讨论】:

  • 请不要在您发布请求 45 分钟后对请求进行重大更改
  • 我刚刚发布了一个示例查询来说明我的意思。顺便说一句,感谢您的提醒。 @t-clausen.dk

标签: sql-server count sql-server-2012 calculated-columns


【解决方案1】:

您可以基于 sys.columns 创建动态 SQL,例如:

declare @columns varchar(8000), @sql varchar(8000)

set @columns = ''

select @columns = @columns + 'case when [' + name + '] > 0 then 1 else 0 end+'
from sys.columns 
where 
    object_id = object_id('TABLENAME') and 
    name not in ('not','wanted','columns') and 
    user_type_id in (select user_type_id from sys.types where name = 'int')


set @sql = 'select CustomerId, ' + @columns + '0 as VALUE from TABLENAME'
exec (@sql)

这种方法当然存在风险,即向表中添加新列可能会导致不需要的结果。

【讨论】:

  • 您能解释一下 (--) 部分中的 user_type_id 部分吗? @JamesZ
  • 这将 SQL 限制为仅 int 类型的列,因此您不会意外计算会导致错误的字符串。其他数字类型也可以,你可以通过 select * from sys.types 看到它们
  • 'select CustomerId, ' + //columns// + '0 as VALUE from myrealtable' 不是有效标识符。我收到此错误@JamesZ
  • 我只能使用一个 at-sign in 评论。我为你使用了一个,所以我不能在 @JamesZ 列之前使用 at-sign
  • 修复了这个问题,并删除了我的一些 cmets 以使其更短。
【解决方案2】:

我怀疑你有 150 列的正当理由。但是,您可以使用 Pivot 计算不为 0 的值的方法:

DECLARE @table TABLE(customer_id int, col1 int, col2 int, 
                     col3 int, col4 int, col5 int);  

INSERT INTO @table
VALUES(1, 1, 2, 3, 4, 5) ,(2, 0, 2, 0, 4, 5) 

SELECT count(CASE WHEN columns <> 0 THEN 1 END), customer_id
FROM @table as p  
UNPIVOT      
(columns FOR Seq IN           
([col1], [col2], [col3], [col4], [col5]) ) AS unpvt  
GROUP BY customer_id

结果:

5   1
3   2

如果要动态选择列:

CREATE TABLE 
  test_table(customer_id int, col1 int, col2 int,
             col3 int, col4 int, col5 int);  

INSERT INTO test_table
VALUES(1, 1, 2, 3, 4, 5) ,(2, 0, 2, 0, 4, 5) ;

DECLARE @columnnames varchar(max)
SELECT @columnnames = coalesce(@columnnames + ',['+ column_name + ']' , '['+ column_name + ']' )
FROM INFORMATION_SCHEMA.Columns 
WHERE
  table_name = 'test_table' and 
  column_name like 'col[0-9]%' and
  table_schema = 'dbo'
ORDER BY column_name

DECLARE @sql varchar(max) = 
'SELECT count(CASE WHEN columns <> 0 THEN 1 END), customer_id
FROM test_table as p  
UNPIVOT      
(columns FOR Seq IN           
('+@columnnames+') ) AS unpvt  
GROUP BY customer_id'

EXEC (@sql)

【讨论】:

    【解决方案3】:

    这是一个在临时表上使用 3 列的基本示例。您可以使用动态 SQL 来适应您的结构。

    样本数据:

    CREATE TABLE #Customers
    (
        CustomerID INT
        , SomeColumn VARCHAR(100)
        , Column1 INT
        , Column2 INT
        , Column3 INT
    );
    
    
    INSERT INTO #Customers
        (CustomerID, SomeColumn, Column1, Column2, Column3)
    VALUES
        (1, 'aaa', 1, 0, 2)
        , (2, 'bbb', 0, 0, 3)
        , (3, 'ccc', 0, 0, 0)
    

    实际查询:

    SELECT CustomerID, SomeColumn, IIF(Column1 > 0, 1, 0) + IIF(Column2 > 0, 1, 0) + IIF(Column3 > 0, 1, 0) AS T
    FROM #Customers
    

    结果如下所示:

    CustomerID  SomeColumn  T
    1           aaa         2
    2           bbb         1
    3           ccc         0
    

    【讨论】:

    • 你为什么使用聚合(MAX)?删除它和 group by 不会有任何区别
    【解决方案4】:

    为了避免我误解你的问题,我尝试创建示例表和数据,基本上我从你的问题中了解到的是,每一列都是整数类型,每当记录大于 0 时设置为 1,否则为 0 ;如果您能够提供样本数据和预期输出,这很好。 :)

    CREATE TABLE tblTEST
    (
    COLUMN1 INT,
    COLUMN2 INT,
    COLUMN3 INT,
    COLUMN4 INT,
    COLUMN5 INT
    )
    
    INSERT INTO tblTEST
    SELECT 1,0,5,12,6
    UNION ALL
    SELECT 1,10,0,12,6
    UNION ALL
    SELECT 1,30,5,0,6
    
    DECLARE @ColumnName NVARCHAR(MAX) = ''
    DECLARE @Table_Name NVARCHAR(1000) = 'TBLTEST'
    DECLARE @Query NVARCHAR(MAX) = ''
    
    DECLARE @nStart INT = 1
    DECLARE @nLast INt = (SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS
    WHERE TABLE_NAME = @Table_Name )
    
    WHILE @nStart <=@nLast
    BEGIN
    
    SET @ColumnName = @ColumnName + ' CASE WHEN '+  (SELECT COLUMN_NAME FROM(SELECT COLUMN_NAME,ROW_NUMBER() OVER(ORDER BY COLUMN_NAME) RN FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @Table_Name) T1 WHERE RN=@nStart) + ' >0 THEN 1 ELSE 0 END '+ (SELECT COLUMN_NAME FROM(SELECT COLUMN_NAME,ROW_NUMBER() OVER(ORDER BY COLUMN_NAME) RN FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @Table_Name) T1 WHERE RN=@nStart) + ','
    
    SET @nStart = @nStart + 1
    END
    
    SET @ColumnName = SUBSTRING(@ColumnName,1,LEN(@ColumnName)-1)
    
    SET @Query = 'SELECT ' + @ColumnName + ' FROM ' + @Table_Name
    
    EXECUTE SP_EXECUTESQL @Query
    

    【讨论】:

      【解决方案5】:

      另一种选择是使用动态sql和while循环

      样本数据

       -- Populate some sample data
      
      IF OBJECT_ID('tempdb..#T','U') IS NOT NULL DROP TABLE #T;
      
      CREATE TABLE #T
       (Column1 INT, Column2 INT, Column3 INT, Column4 INT, Column5 INT);
      
      INSERT INTO #T VALUES 
       (0,0,0,1,0), 
       (0,1,0,1,0), 
       (1,0,0,2,0), 
       (1,0,0,0,1);
      

      带有 while 循环的动态 SQL

      DECLARE @ResultTable TABLE (HasZeroValue TINYINT);
      -- Number of columns to search for zero values
      DECLARE @ColumnsCount INT = 5;
      -- Dynamic sql statement
      DECLARE @SQL NVARCHAR(MAX);
      
      DECLARE @i INT = 1;
      
      WHILE @i <= @ColumnsCount
      BEGIN
      
       SET @SQL = 'SELECT CASE COUNT(*) WHEN 0 THEN 0 ELSE 1 END FROM #T WHERE Column' + CAST(@i AS VARCHAR) + ' > 0;'; 
      
       INSERT @ResultTable
       EXEC sp_executesql @SQL;
      
       SET @i = @i + 1;
      
      END
      
      SELECT SUM(HasZeroValue) FROM @ResultTable;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-15
        • 1970-01-01
        • 1970-01-01
        • 2015-10-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多