动态搜索给定表的列

时间:2012-07-01 13:39:44

标签: sql sql-server-2008 tsql stored-procedures dynamic-sql

我需要创建一个搜索我正在构建的Java应用程序,用户可以根据他们当前正在查看的表以及他们提供的搜索词来搜索SQL数据库。起初我会做这样简单的事情:

SELECT * FROM <table name> WHERE CAST((SELECT COLUMN_NAME 
  FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '<table name>') 
  AS VARCHAR) LIKE '%<search term>%'

但该子查询返回多个结果,因此我尝试创建一个过程来遍历给定表中的所有列,并将任何相关字段放在结果表中,如下所示:

CREATE PROC sp_search
    @tblname VARCHAR(4000),
    @term VARCHAR(4000)
AS
    SET nocount on
    SELECT COLUMN_NAME 
    INTO #tempcolumns
    FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @tblname

    ALTER TABLE #tempcolumns
    ADD printed BIT,
        num SMALLINT IDENTITY

    UPDATE #tempcolumns
        SET printed = 0

    DECLARE @colname VARCHAR(4000),
        @num SMALLINT
    WHILE EXISTS(SELECT MIN(num) FROM #tempcolumns WHERE printed = 0)
    BEGIN
        SELECT @num = MIN(num) 
            FROM #tempcolumns 
            WHERE printed = 0
        SELECT @colname = COLUMN_NAME 
            FROM #tempcolumns 
            WHERE num = @num
        SELECT * INTO #results FROM @tblname WHERE CAST(@colname AS VARCHAR) 
                    LIKE '%' + @term + '%' --this is where I'm having trouble
        UPDATE #tempcolumns
            SET printed = 1
            WHERE @num = num

    END

    SELECT * FROM #results
GO

这有两个问题:首先是它以某种方式陷入无限循环,其次我无法从@tblname中选择任何东西。我也试过使用动态sql,但我不知道如何从中得到结果,或者甚至可能。

这是我在大学时所做的一项任务,经过几个小时的努力来解决这个问题后,我已经做到了这一点。有什么办法可以做我想做的事吗?

2 个答案:

答案 0 :(得分:0)

你进入一个无限循环,因为即使没有匹配,EXISTS(SELECT MIN(num) FROM #tempcolumns WHERE printed = 0)总会返回一行 - 你需要EXISTS (SELECT * ....而不是

要使用动态SQL,您需要构建要运行的SQL语句的字符串(varchar),然后使用EXEC

调用它

例如:

 declare @s varchar(max)
 select @s = 'SELECT * FROM mytable '
 Exec (@s)

答案 1 :(得分:0)

  1. 您只需要搜索实际包含字符串的列,而不是搜索表中的所有列(可能包括整数,日期,GUID等)。
  2. 你根本不需要#temp表(当然也不是## temp表)。
  3. 您需要使用动态SQL(虽然我不确定这是否已成为您课程的一部分)。
  4. 我认为follow a few simple conventions对您有所侵犯是有益的:
    • 使用PROCEDURE而非PROC - 它不是“prock”,而是“存储过程”。
    • use dbo. (or alternate schema) prefix when referencing any object
    • 将您的程序正文包裹在BEGIN / END
    • 大量使用元音。您是否保存了那么多次击键,从不介意时间,说@tblname而不是@tablename@table_name?我不是为了一个特定的会议而奋斗,但是以可读性为代价来节省字符在70年代失去了它的魅力。
    • 不要对存储过程使用sp_前缀 - 此前缀在SQL Server中具有特殊含义。为该程序命名程序。它不需要前缀,就像我们知道即使没有tbl前缀它们也是表。如果您确实需要一个前缀,请使用usp_proc_之类的前缀,但我个人认为该前缀不会为您提供任何您尚未拥有的信息。
    • 由于表格是使用Unicode存储的(有些列可能也是如此),因此参数应为NVARCHAR,而不是VARCHAR。标识符的上限为128个字符,因此没有理由支持&gt; @tablename的257个字符。
    • terminate statements with semi-colons
    • use the catalog views instead of INFORMATION_SCHEMA - 虽然后者是你的教授所教导和可能期望的。
  5. CREATE PROCEDURE dbo.SearchTable
        @tablename NVARCHAR(257),
        @term      NVARCHAR(4000)
    AS
    BEGIN
        SET NOCOUNT ON;
    
        DECLARE @sql NVARCHAR(MAX);
    
        SET @sql = N'SELECT * FROM ' + @tablename + ' WHERE 1 = 0'; 
    
        SELECT @sql = @sql + ' 
          OR ' + c.name + ' LIKE ''%' + REPLACE(@term, '''', '''''') + '%'''
        FROM 
          sys.all_columns AS c
        INNER JOIN 
          sys.types AS t
          ON c.system_type_id = t.system_type_id
          AND c.user_type_id = t.user_type_id
        WHERE 
          c.[object_id] = OBJECT_ID(@tablename)
          AND t.name IN (N'sysname', N'char', N'nchar', 
            N'varchar', N'nvarchar', N'text', N'ntext');
    
        PRINT @sql;
    
        -- EXEC sp_executesql @sql;
    END
    GO
    

    如果您对正在输出的SELECT查询感到满意,请注释掉PRINT并取消注释EXEC