Использование SQL Server sp_msforeachtable для выбора только тех таблиц, которые удовлетворяют некоторому условию - PullRequest
17 голосов
/ 13 марта 2012

Я пытаюсь написать этот запрос, чтобы найти все таблицы с определенным столбцом с определенным значением.Это то, что я сделал до сих пор -

EXEC sp_MSforeachtable 
@command1='
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=PARSENAME("?",2) AND TABLE_NAME=PARSENAME("?",1) AND COLUMN_NAME="EMP_CODE")
BEGIN
    IF (SELECT COUNT(*) FROM ? WHERE EMP_CODE="HO081")>0
    BEGIN
        SELECT * FROM ? WHERE EMP_CODE="HO081"
    END
END
'

Я надеюсь, что мои намерения ясны, я просто хочу выбрать только те таблицы, в которых присутствует столбец EMP_CODE и в тех таблицах, которые я хочу выбратьте строки, где EMP_CODE='HO081'.

Edit -

Теперь это выглядит так.Но я не могу заменить @EMPCODE переменную в запросе.

DECLARE @EMPCODE AS VARCHAR(20)
SET @EMPCODE='HO081'
EXEC sp_MSforeachtable 
@command1='
    DECLARE @COUNT AS INT
    SELECT @COUNT=COUNT(*) FROM ? WHERE EMP_CODE='''+@EMPCODE+'''
    IF @COUNT>0
    BEGIN
        PRINT PARSENAME("?",1)+'' => ''+CONVERT(VARCHAR,@COUNT)+'' ROW(S)''
        --PRINT ''DELETE FROM ''+PARSENAME("?",1)+'' WHERE EMP_CODE='''''+@EMPCODE+'''''''
    END
',@whereand='AND O.ID IN (SELECT OBJECT_ID FROM SYS.COLUMNS C WHERE C.NAME='''+@EMPCODE+''')'

Ответы [ 2 ]

47 голосов
/ 13 марта 2012

Вы знаете, как sp_MSforeachtable недокументировано и может уйти в любое время / быть измененным?

Что ж, если вы будете счастливы игнорировать это, у него есть другой параметр, называемый @whereand, которыйдобавляется к предложению WHERE внутреннего запроса, который используется для поиска таблиц (и должен начинаться с AND).

Вы также должны знать, что существует псевдоним oпротив sysobjects и второго псевдонима syso против sys.all_objects.

Используя эти знания, вы можете создать свой параметр @whereand как:

EXEC sp_MSforeachtable 
@command1='...',
@whereand='AND o.id in (select object_id from sys.columns c where c.name=''EMP_CODE'')'

Теперь вы также можетеупростите ваш command1, поскольку вы знаете, что он будет работать только с таблицами, содержащими столбец EMP_CODE.Возможно, я бы также снял условие COUNT(*), так как не вижу, какое значение оно добавляет.


Обновлено на основе вашей дальнейшей работы и проверено на одной таблице:

DECLARE @EMPCODE AS VARCHAR(20)
SET @EMPCODE='HO081'
declare @sql nvarchar(2000)
set @sql = '
    DECLARE @COUNT AS INT
    SELECT @COUNT=COUNT(*) FROM ? WHERE EMP_CODE='''+@EMPCODE+'''
    IF @COUNT>0
    BEGIN
        PRINT PARSENAME("?",1)+'' => ''+CONVERT(VARCHAR,@COUNT)+'' ROW(S)''
        --PRINT ''DELETE FROM ''+PARSENAME("?",1)+'' WHERE EMP_CODE='''''+@EMPCODE+'''''''
    END
'
EXEC sp_MSforeachtable 
@command1=@sql,@whereand='AND O.ID IN (SELECT OBJECT_ID FROM SYS.COLUMNS C WHERE C.NAME=''EMP_CODE'')'

(я вернул @whereand, чтобы запросить EMP_CODE, так как вы не хотите заменять значение там).

Проблема в том, что вы можете передать параметры для хранимой процедуры или литералы , но вы не можете выполнять вычисления / комбинировать действия между ними - поэтому я перенес конструкцию оператора sql в отдельное действие.

9 голосов
/ 13 марта 2012

Полагаю, вы получили какую-то ошибку, возможно, Invalid column name 'EMP_CODE'?

Это потому, что код компилируется перед проверкой столбца.Вы можете сделать это вместо этого.

EXEC sp_MSforeachtable 
@command1='
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=PARSENAME("?",2) AND TABLE_NAME=PARSENAME("?",1) AND COLUMN_NAME="EMP_CODE")
BEGIN
   EXEC(''
          IF (SELECT COUNT(*) FROM ? WHERE EMP_CODE="HO081")>0
          BEGIN
              SELECT * FROM ? WHERE EMP_CODE="HO081"
          END
        '')
END
'
...