【问题标题】:Select column names that match a criteria (MS SQL Server)选择符合条件的列名 (MS SQL Server)
【发布时间】:2013-07-19 07:26:39
【问题描述】:

我在 MS SQL Server 中有以下数据库结构: ID、Col_A、Col_B、Col_C 等...

除 ID 之外的所有其他列都是布尔类型。例如,让我们说 Col_A = 1, Col_B = 0, Col_C = 1

我正在寻找一种方法来返回列为 1 的列的名称。 在此示例中,返回值应类似于 ID、Col_A、Col_C

会有动态数量的列,因为表经常被更改以添加新列并删除旧列。

基本上,我需要与以下帖子中完全相同的功能,但作为 MS Sql Server 查询: Select column names that match a criteria (MySQL)

SQL Fiddle 链接http://sqlfiddle.com/#!2/8f4ee/12 是我想在 MS SQL Server 中实现的。有什么想法我会怎么做? MS SQL Server 无法识别 CONCAT_WS 和 GROUP_CONCAT 这两个函数。

任何帮助将不胜感激。

【问题讨论】:

  • edit你的问题在这里提问(包括代码的相关部分)。如果大部分内容都在其他地方的链接中,那么未来的读者将无法搜索到,并且如果链接(尤其是指向场外的链接)不可用,这也是毫无意义的。这里的问题应该是独立的,任何链接都是额外的参考;链接不应是主要内容。谢谢。
  • 您想要每个 ID 的结果?

标签: sql sql-server


【解决方案1】:

试试这个:

create table jtr (id varchar(36), col_a tinyint, col_b tinyint, col_c tinyint)
insert into jtr values (1, 1, 0, 1), (2, 0, 0, 1), (3, 1, 0 ,0), (4, 0, 1, 0)

CREATE TABLE #app (res tinyint)
CREATE TABLE #outmess (message varchar(1000))

DECLARE @dynsql varchar(1000)
DECLARE @colname sysname
DECLARE @mess varchar(1000)
DECLARE @id int

DECLARE #crs_tab INSENSITIVE CURSOR FOR
SELECT id FROM jtr
FOR READ ONLY

OPEN #crs_tab
FETCH NEXT FROM #crs_tab INTO @id

WHILE (@@FETCH_STATUS = 0)
BEGIN
    DECLARE #crs INSENSITIVE CURSOR FOR
    SELECT c.name FROM syscolumns c
    JOIN sysobjects o
    ON o.id = c.id
    WHERE o.name = 'jtr'
    AND o.xtype = 'U'
    AND c.type = 38
    FOR READ ONLY

    OPEN #crs
    FETCH NEXT FROM #crs INTO @colname
    WHILE (@@FETCH_STATUS = 0)
    BEGIN
        SET @dynsql = 'SELECT ' + @colname + ' FROM jtr where id = ' + CONVERT(varchar, @id)
        insert into #app
        exec (@dynsql)

        if (select res from #app) = 1
        begin
            if (@mess != '')
            begin
                set @mess = @mess + ', ' + @colname
            end
            else
            begin
                set @mess = @colname
            end
        end

        delete from #app
        FETCH NEXT FROM #crs INTO @colname
    END
    CLOSE #crs
    DEALLOCATE #crs
    insert into #outmess values (@mess)

    set @mess = ''
    FETCH NEXT FROM #crs_tab INTO @id
END
CLOSE #crs_tab
DEALLOCATE #crs_tab

select * from #outmess

我已经创建了一个示例表 JTR,其中包含以下字段:ID、COL_A、COL_B、COL_C,如果您愿意,可以放置所需的字段数。

我在那个表中放了 4 行。

所以我实现了两个游标,一个用于滚动 JTR 表,一个用于滚动 syscolumns 中的所有列名。

对于每一个值为 ONE 的列,我用列名构建一条消息

在临时表#outmess 中,您会发现所有值为 ONE 的列以逗号连接。

附:取而代之的是布尔值,我使用了tinyint。在 syscolumns 字段类型值 38 关于 tinyint

告诉我是否可以

【讨论】:

    【解决方案2】:

    这很简单(SQLFiddle:http://sqlfiddle.com/#!3/c44bc/1/0):

    DECLARE @cmd NVARCHAR(MAX);
    DECLARE @cmd_partial NVARCHAR(MAX);
    
    SET @cmd_partial = '';
    
    SELECT @cmd_partial = @cmd_partial + '
           CASE ' + COLUMN_NAME + '
              WHEN 1 THEN
                 ''' + COLUMN_NAME + ', ''
              ELSE
                 ''''
           END +'
      FROM INFORMATION_SCHEMA.COLUMNS
     WHERE TABLE_NAME = 'mytable'
       AND COLUMN_NAME <> 'id';
    
    SET @cmd_partial = LEFT(@cmd_partial, LEN(@cmd_partial) - 1);
    
    SET @cmd = '
    WITH cols AS (
     SELECT id,
    ' + @cmd_partial + ' AS columns
    FROM mytable
    )
      SELECT id, CASE
                    WHEN LEN(columns) > 0 THEN
                       LEFT(columns, LEN(columns) - 1)
                    ELSE
                       ''''
                 END
        FROM cols;
    ';
    
    EXEC (@cmd);
    

    【讨论】:

    • 这正是我想要的。谢谢!
    【解决方案3】:

    以下查询与您的 MySql 查询非常等价。 Working demo

    查询:

      declare @sql nvarchar(max)
      select @sql = ''
      select @sql = @sql +   case WHEN len(@sql) = 0 THEN '' ELSE ' + ' END + 
      ' case when ' + C.Name  + ' = 1 then '',' + C.Name  + ''' else '''' end'
      FROM
        sys.columns c
        inner join sys.objects o on o.Object_ID = c.Object_ID 
      WHERE
        o.Name ='yourtable'
        AND C.Name!='id'
    
    
      select @sql = 'select ID, Stuff(' + @sql + ',1,1,N'''') from yourtable'
      exec (@sql)
    

    【讨论】:

      【解决方案4】:

      其中一种可能的解决方案是动态 sql 取消透视数据并进一步连接:

      declare @stmt nvarchar(max)
      
      select @stmt = IsNull(@stmt + ', ', '') + quotename(name)
      from sys.columns
      where object_id = object_id('TableName') and name != 'ID'
      order by column_id
      
      set @stmt = ';with du as (
          select *
          from TableName d
              unpivot (Val for Name in (' + @stmt + ')) u
      )
      select d.ID, IsNull(left(Cols, len(Cols) - 1), ''<None>'') as ColumnsSet
      from (select ID from TableName) d
          cross apply (select (select Name + '', ''
              from du
              where Val = 1 and ID = d.ID
              for xml path ('''')) as Cols) c'
      
      print @stmt
      exec(@stmt)
      

      对于像这样的数据:

      create table TableName (ID int, Col1 bit, Col2 bit, Col3 bit)
      insert into TableName values
          (1, 0, 1, 1)
          ,(2, 1, 1, 1)
          ,(3, 1, 0, 0)
          ,(4, 0, 0, 0)
      

      输出是:

      ID          ColumnsSet
      ----------- -----------------
      1           Col2, Col3
      2           Col1, Col2, Col3
      3           Col1
      4           <None>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-02-21
        • 2016-06-24
        • 2021-09-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多