web-dev-qa-db-fra.com

Utilisation de sp_msforeachtable de SQL Server pour sélectionner uniquement les tables qui remplissent certaines conditions

J'essaie d'écrire cette requête pour trouver toutes les tables avec une colonne spécifique avec une valeur spécifique. C'est ce que j'ai fait jusqu'à présent -

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
'

J'espère que mes intentions sont claires, je veux juste sélectionner uniquement les tables où la colonne EMP_CODE est présent et dans ces tableaux, je veux sélectionner les lignes où EMP_CODE='HO081'.

Modifier -

Maintenant, ça ressemble à ça. Mais je ne peux pas remplacer @EMPCODE variable dans la requête.

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+''')'
17
Soham Dasgupta

Vous savez comment sp_MSforeachtable N'est pas documenté et peut disparaître à tout moment/être modifié?

Eh bien, si vous êtes content de l'ignorer, il a un autre paramètre appelé @whereand, Qui est ajouté à la clause WHERE de la requête interne qui est utilisée pour trouver les tables (et devrait commencer par un AND).

Vous devez également savoir qu'il existe un alias, o contre sysobjects, et un deuxième alias syso contre sys.all_objects.

En utilisant ces connaissances, vous pouvez créer votre paramètre @whereand Comme:

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

Vous pouvez désormais également simplifier votre command1, Car vous savez qu'il ne sera exécuté que sur des tables contenant une colonne EMP_CODE. Je supprimerais probablement également la condition COUNT(*), car je ne vois pas quelle valeur il ajoute.


Mis à jour en fonction de vos travaux ultérieurs et testé sur une table:

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'')'

(J'ai rétabli le @whereand Pour rechercher EMP_CODE, Car vous ne voulez pas remplacer la valeur ici).

Le problème est que vous pouvez passer paramètres à une procédure stockée, ou littéraux, mais vous ne pouvez pas effectuer de calculs/combiner des actions entre eux - j'ai donc déplacé la construction de l'instruction sql dans une action distincte.

47

Je suppose que vous obtenez une erreur quelconque, peut-être Invalid column name 'EMP_CODE'?

C'est parce que le code est compilé avant de vérifier la colonne. Vous pourriez faire comme ça à la place.

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
'
9
Mikael Eriksson