【问题标题】:How can I return a distinct count of a variable for all tables in my database?如何为数据库中的所有表返回不同的变量计数?
【发布时间】:2019-03-09 02:47:24
【问题描述】:

我有一个包含 60 多个表的 SQL 数据库,几乎所有表都填充了 CLIENTID 字段。我想计算每个表中唯一客户 ID 的数量。

我正在寻找的结果是:

TABLE_NAME; CLIENTID_COUNT
dbo.HISTORY; 650
dbo.VISITS; 596
dbo.SALES; 1053
...; ...

这似乎应该如此简单,但我已经玩了几个小时的光标,无法弄清楚这一点。请帮忙!

【问题讨论】:

标签: sql sql-server


【解决方案1】:

与此处其他答案的模式类似,但这是我将如何解决它:

IF OBJECT_ID('#Tables', 'U') IS NOT NULL
    DROP TABLE #Tables;
SELECT ID = IDENTITY(INT, 1, 1),
    SchemaName = OBJECT_SCHEMA_NAME([object_id]),
    TableName = OBJECT_NAME([object_id]),
    ColumnName = name,
    DistinctCount = 0
    INTO #Tables
    FROM sys.columns
    WHERE name = 'CLIENTID';

DECLARE @ID INT = 1,
    @MaxID INT = (SELECT MAX(ID) FROM #Tables);
WHILE @ID < @MaxID
BEGIN;
    DECLARE @SQLCommand VARCHAR(MAX);
    SELECT @SQLCommand = FORMATMESSAGE('
UPDATE #Tables SET DistinctCount = (
    SELECT COUNT(DISTINCT %s) FROM %s.%s
    )
    WHERE ID = %i;',
        QUOTENAME(ColumnName), QUOTENAME(SchemaName), QUOTENAME(TableName), ID)
        FROM #Tables
        WHERE ID = @ID;
    EXEC (@SQLCommand);
    SET @ID += 1;
END;

SELECT *
    FROM #Tables;

【讨论】:

    【解决方案2】:

    我真的不喜欢游标和循环。尽管从性能角度来看这不会有太大差异,但我想分享如何利用系统表和动态 sql 来避免使用游标、while 循环或临时表来处理类似的事情。

    此代码实际上就是您执行此操作所需的全部内容。

    declare @SQL nvarchar(max) = ''
    
    select @SQL = @SQL + 'select TableName = ''' + t.name + ''', ClientID_Count = count(distinct clientID)
    from ' + QUOTENAME(t.name) + ' UNION ALL ' 
    from sys.tables t
    join sys.columns c on c.object_id = t.object_id
    where c.name = 'clientID'
    
    select @SQL = left(@SQL, len(@SQL) - 10) --removes the last UNION ALL
    
    select @SQL
    --once your comfortable the dynamic sql is correct just uncomment the line below.
    --exec sp_executesql @SQL
    

    【讨论】:

      【解决方案3】:

      你可以使用动态sql。

      这将读取您的系统表,找到具有 ClientID 列的表,并从每个表中构建一般形式为“Select Count(DISTINCT ClientID)”的查询文本。

      DECLARE @SQLQuery as nvarchar(max) = ''
      
      ------------------------------------
      -- GET THE TABLES THAT HAVE A CLIENTID FROM SCHEMA
      SELECT @SQLQuery = @SQLQuery + qryTxt FROM (
          SELECT DISTINCT 'SELECT ''' + tables.name + ''', COUNT(DISTINCT CLIENTID) FROM ' + tables.name + ' UNION ' AS qryTxt
          FROM sys.columns left join sys.tables on columns.object_id = tables.object_id where columns.name = CLIENTID AND isnull(tables.name, '') <> '') subquery
      
      ------------------------------------
      -- REMOVE THE LAST 'UNION' KEYWORD FROM SQLQUERY 
      SET @SQLQuery = left(@sqlQuery, len(@sqlQuery) - 5)
      
      ------------------------------------
      -- EXECUTE
      execute sp_executesql @SQLQuery
      

      【讨论】:

      • LOL 你和我发布了非常相似的解决方案,相隔几秒钟。 :)
      • 哇!这是对我的赞美。
      【解决方案4】:

      假设每个表中的列都是 ClientId,您应该可以按原样使用它:

      DROP TABLE IF EXISTS #clientId
      CREATE TABLE #clientId
      (
          TableName nvarchar(1000),
          ClientIdCount bigint
      )
      
      DECLARE @TableName nvarchar(1000);
      DECLARE @CurrentQuery nvarchar(2000);
      
      DECLARE result_cursor CURSOR local fast_forward FOR
      SELECT DISTINCT
          '['+TABLE_SCHEMA + '].[' + TABLE_NAME + ']'
      FROM
          INFORMATION_SCHEMA.COLUMNS
      WHERE
          COLUMN_NAME = 'ClientId'
      
      OPEN result_cursor
      FETCH NEXT FROM result_cursor into @TableName
      WHILE @@FETCH_STATUS = 0
      BEGIN 
      
      SET @CurrentQuery = 'SELECT ''' + @TableName + ''', COUNT(DISTINCT ClientId) FROM ' + @TableName
      --print @CurrentQuery
      
      INSERT INTO
          #clientId
      (
          TableName,
          ClientIdCount
      )
      EXEC(@CurrentQuery)
      
      FETCH NEXT FROM result_cursor into @TableName
      END
      --end loop
      
      --clean up
      CLOSE result_cursor
      DEALLOCATE result_cursor
      
      GO
      
      SELECT
          *
      FROM
          #clientId
      

      【讨论】:

        【解决方案5】:
        IF OBJECT_ID('tempdb..#temp_RESULTS') IS NOT NULL DROP TABLE #temp_RESULTS
        CREATE TABLE #TEMP_RESULTS
        (
        TABLENAME VARCHAR(MAX),
        CLIENTCNT BIGINT
        )
        
        DECLARE @TABLENAME VARCHAR(MAX)
        DECLARE @command VARCHAR(MAX)
        
        IF OBJECT_ID('tempdb..#temp_PROCESS') IS NOT NULL DROP TABLE #temp_PROCESS
        SELECT * INTO #TEMP_PROCESS FROM sys.tables 
        
        WHILE EXISTS(SELECT * FROM [#TEMP_PROCESS])
        BEGIN
        SET @TABLENAME = (SELECT TOP 1 [NAME] FROM [#TEMP_PROCESS])
        SET @command = ('SELECT ''' + @TABLENAME + ''', COUNT(DISTINCT CLIENTID) AS CLIENTCNT FROM ' + @TABLENAME)
        SELECT @command
        INSERT INTO #TEMP_RESULTS
        EXEC(@command) 
        
        DELETE FROM [#TEMP_PROCESS] WHERE [NAME] = @TABLENAME
        END
        
        SELECT * FROM [#TEMP_RESULTS]
        

        【讨论】:

        • 我认为插入实际上应该是 "SELECT ''' + @TABLENAME + ''', COUNT(DISTINCT CLIENTID) AS CLIENTCNT FROM ' + @TABLENAME"
        猜你喜欢
        • 1970-01-01
        • 2020-04-22
        • 1970-01-01
        • 2019-02-08
        • 1970-01-01
        • 2017-08-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多