【问题标题】:Identify all columns containing only a single value (e.g. 0,0.00, Z, Y, N)识别仅包含单个值的所有列(例如 0,0.00、Z、Y、N)
【发布时间】:2020-10-29 21:08:32
【问题描述】:

我找到了识别具有重复值的列的方法,并且我可以使用 Select Distinct 来查找只有一个值的列,但是我有很多包含数百列甚至数百万行的表。我需要找到该列仅包含 0、0.00、Z、N 或 Y 的每个列名。本质上是试图创建一个无用列的列表。

如何在每一列、一次一列中迭代 select distinct(或沿着这些线的东西),并在表中识别每行中的列值都相同的那些实例?或者,行数小于 2 的实例。

这似乎有点接近,但指定值无济于事: SQL Server SELECT where any column contains 'x'

分组并拥有 > 1 种作品,但我必须为每一列运行它。

Select Distinct 有效,但我必须一次运行一列,除非我缺少更好的方法。

我有一些方法可以合作,但仅限于数字列:Find columns that contain only zeros

这可以识别仅包含 NULL 的列,但我似乎无法让它对任何其他值起作用:

DECLARE crs CURSOR LOCAL FAST_FORWARD FOR SELECT name FROM syscolumns WHERE id=OBJECT_ID('TABLENAME')
OPEN crs
DECLARE @name sysname
FETCH NEXT FROM crs INTO @name
WHILE @@FETCH_STATUS = 0
BEGIN
    EXEC('SELECT ''' + @name + ''' WHERE NOT EXISTS (SELECT * FROM TABLENAME WHERE ' + @name + ' IS NOT NULL)')
    FETCH NEXT FROM crs INTO @name
END
CLOSE crs
DEALLOCATE crs

我已经开始适应了一点,但在 from:

附近遇到了关于不正确语法的错误
DECLARE crs CURSOR LOCAL FAST_FORWARD FOR SELECT name FROM syscolumns WHERE id=OBJECT_ID('TABLENAME')
DECLARE @sql NVARCHAR(MAX)
OPEN crs
DECLARE @name sysname
FETCH NEXT FROM crs INTO @name
WHILE @@FETCH_STATUS = 0
BEGIN
Create Table #Temp (ID NVARCHAR, Info INT)
SET @sql = N'select distinct ''' + @name + ''' insert into #Temp (ID) from TABLENAME; SELECT COUNT(*) from #Temp Having COUNT(*)<2 insert into  #Temp (Info);' 
EXEC sp_executesql @sql 
DROP TABLE #Temp
FETCH NEXT FROM crs INTO @name
END
CLOSE crs
DEALLOCATE crs

【问题讨论】:

  • 实现这一目标的唯一方法是使用动态 SQL。然而,这似乎是一个奇怪的要求。为什么你需要找出这个?你打算如何处理这些信息?这听起来像是开始,或结束,或XY Problem
  • 我需要减少列数,以便我们可以更好地将这个旧数据库与新数据库对齐。因此,所有这些只有零或 0.00 的列都没有提供任何信息,并且使得筛选旧数据和将旧数据与新数据对齐变得更加困难。在具有 10 或 20 列的较小表上很容易,但在有 200 列且只有 20 列有有用信息的情况下,排序太多了。

标签: sql-server


【解决方案1】:

这是您可以解决此问题的一种方法。对于指定表的每一列,它会计算符合您的条件的行数,如果该列的行数与表的行数匹配,则将该列添加到 XML 列表中以供审核。

注意:

鉴于您的列数和可能的行数,此过程可能需要一段时间。

DECLARE @Columns table ( column_name varchar(255), pk INT IDENTITY(1,1) );
DECLARE 
    @TableName varchar(50) = 'tblClients',
    @Criteria varchar(255) = 'IN (''0'', ''0.00'', ''Z'', ''N'', ''Y'')',
    @sql nvarchar(MAX),
    @rows int;

/* Get the current row count for @TableName */

SET @sql = 'SELECT @count = COUNT( DISTINCT [' + @col + '] ) FROM ' + @TableName + ';';
    EXEC sp_executesql @sql, N'@count int OUT', @count = @rows OUT;

/* Create an XML object to store fields that match criteria */
DECLARE @List xml = '<root><columns></columns></root>';

    -- Insert the table name and number of rows processed.
    SET @List.modify( 
      'insert
        <table>
          <name>{sql:variable("@TableName")}</name>
          <rows>{sql:variable("@rows")}</rows>
          <criteria>{sql:variable("@Criteria")}</criteria>
        </table>
      as first
      into (//root)[1]' 
    );

/* Collect a list of column names for the specified table */

INSERT INTO @Columns ( column_name )
SELECT [name] FROM sys.columns WHERE object_id = OBJECT_ID( @TableName ) ORDER BY column_id;

/* For-each column, check if it contains: [ 0 | 0.00 | Z | N | Y ] */

DECLARE @pk int = 1, @col varchar(255), @count int;
WHILE @pk <= ( SELECT MAX ( pk ) FROM @Columns ) 
BEGIN

    -- Current column.
    SELECT @col = column_name FROM @Columns WHERE pk = @pk;
    
    -- Get count of rows for this column with the specified values.
    SET @sql = 'SELECT @count = COUNT( DISTINCT [' + @col + '] ) FROM ' + @TableName + ' WITH (NOLOCK) WHERE CAST( [' + @col + '] AS varchar(MAX) ) ' + @Criteria + ';';
        EXEC sp_executesql @sql, N'@count int OUT', @count = @count OUT;

    -- Insert the column for review if its count matches the table row count.
    IF @count BETWEEN 1 AND 2
        SET @List.modify(
          'insert 
            <column>
              <name>{sql:variable("@col")}</name>
              <count>{sql:variable("@count")}</count>
            </column> 
          into (//root/columns)[1]'
        );

    -- Next column.
    SET @pk = @pk + 1;

END

/* SELECT @List results */
SELECT @List AS List;

在我的例子中,返回以下 XML。这些列符合您的审核标准。

<root>
  <table>
    <name>tblClients</name>
    <rows>143</rows>
    <criteria>IN ('0', '0.00', 'Z', 'N', 'Y')</criteria>
  </table>
  <columns>
    <column>
      <name>WebPortal</name>
      <count>143</count>
    </column>
    <column>
      <name>EnforceQTY</name>
      <count>143</count>
    </column>
    <column>
      <name>CheckForPrepaid</name>
      <count>143</count>
    </column>
    <column>
      <name>ReportNet</name>
      <count>143</count>
    </column>
    <column>
      <name>ActiveClient</name>
      <count>143</count>
    </column>
  </columns>
</root>

【讨论】:

  • 我认为这越来越接近了。但是,我认为我需要查找不同行数小于等于 2 的实例,而不是检查列的行数是否 = 表的行数。相当规律地,每一行都将填充默认值下来的路。在原始问题中发布了一些我正在处理的代码。
  • @DavidOverfelt 很简单。我已经更新了我的示例来做到这一点。看看吧。
  • 使用@count BETWEEN 1 AND 2,因为如果没有数据符合给定列的条件,则返回零。
【解决方案2】:

假设这是一次性的,构建一个像这样的临时查询可能更容易和更快

SELECT
  SUM(CASE WHEN Col1 NOT IN ('0.00', 'Z', 'N', 'Y') THEN 1 ELSE 0 END) AS Col1,
  SUM(CASE WHEN Col2 NOT IN ('0.00', 'Z', 'N', 'Y') THEN 1 ELSE 0 END) AS Col2,
  SUM(CASE WHEN Col3 NOT IN ('0.00', 'Z', 'N', 'Y') THEN 1 ELSE 0 END) AS Col3
FROM
yourtable

总数 >= 1 的任何列都有有用的数据。

注意事项

  • 以上内容是一个隐式分组,因为它实际上并未对任何字段进行分组。
  • 以上假设所有列都是字符串(例如,varchar/nvarchar)。如果有混合,您需要按列调整它
  • NULL 也不算作有效值(例如,不要在总数中加 1)

要在 SSMS 中获取您的列列表,您可以这样做

  • 为表格编写脚本(右键单击表格 -> 将表格编写为 -> 创建表格),或者
  • 只需打开左侧的表格,右侧的新查询窗口,然后将“列”一词拖到右侧

然后,只需使用您喜欢的文本编辑器或类似工具,使用上面的模板创建您的 SQL - 将 Col1、Col2 等替换为实际的列名。如果您有字符串和数字类型的混合,第一个选项可用于将它们组合在一起以构造“IN”表达式。

【讨论】:

  • 如果我可以生成 SUM 语句可能是可行的,但我想我可能有一个方法。需要在至少 500 列上执行此操作,所有列都混合为数字、varchar 等。
  • 一种方法是将列列表复制到 Excel 中,然后在下一列中使用像 ="SUM(CASE WHEN [" &amp; A1 &amp; "] NOT IN ... 这样的公式 - 然后将该公式拖到所有行中。如果您可以获得所有列名及其类型(例如,A 列),您可以使用文本函数将 B 列和 C 列转换为列名,然后将它们的一般类型(字符串、数字等)转换为 C,然后D 列具有上面的公式,可基于 IF 语句创建 SUM(CASE()) 表达式,以首先确定它们的类型,例如,如果是数字,则检查 = 0;如果是字符串,则检查 IN ('z', 'n', 'y')
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-19
  • 2015-04-19
  • 1970-01-01
相关资源
最近更新 更多