web-dev-qa-db-fra.com

Comment écrire un foreach dans SQL Server?

J'essaie de réaliser quelque chose comme une for-each, où j'aimerais prendre les identifiants d'une instruction select retournée et utiliser chacun d'eux.

DECLARE @i int
DECLARE @PractitionerId int
DECLARE @numrows int
DECLARE @Practitioner TABLE (
    idx smallint Primary Key IDENTITY(1,1)
    , PractitionerId int
)

INSERT @Practitioner
SELECT distinct PractitionerId FROM Practitioner

SET @i = 1
SET @numrows = (SELECT COUNT(*) FROM Practitioner)
IF @numrows > 0
    WHILE (@i <= (SELECT MAX(idx) FROM Practitioner))
    BEGIN

        SET @PractitionerId = (SELECT PractitionerId FROM @Practitioner WHERE idx = @i)

        --Do something with Id here
        PRINT @PractitionerId

        SET @i = @i + 1
    END

Pour le moment, j'ai quelque chose qui ressemble à ce qui précède, mais j'obtiens l'erreur:

Nom de colonne non valide 'idx'.

Quelqu'un pourrait-il 

143
Pomster

Vous semblez vouloir utiliser un CURSOR. Bien que la plupart du temps, il vaut mieux utiliser une solution basée sur un ensemble, il arrive parfois que CURSOR soit la meilleure solution. Sans en savoir plus sur votre vrai problème, nous ne pouvons pas vous aider davantage:

DECLARE @PractitionerId int

DECLARE MY_CURSOR CURSOR 
  LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR 
SELECT DISTINCT PractitionerId 
FROM Practitioner

OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO @PractitionerId
WHILE @@FETCH_STATUS = 0
BEGIN 
    --Do something with Id here
    PRINT @PractitionerId
    FETCH NEXT FROM MY_CURSOR INTO @PractitionerId
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR
275
Lamak

Supposons que la colonne PractitionerId soit unique, vous pouvez alors utiliser la boucle suivante

DECLARE @PractitionerId int = 0
WHILE(1 = 1)
BEGIN
  SELECT @PractitionerId = MIN(PractitionerId)
  FROM dbo.Practitioner WHERE PractitionerId > @PractitionerId
  IF @PractitionerId IS NULL BREAK
  SELECT @PractitionerId
END
113
Aleksandr Fedorenko

Votre nombre de sélections et de sélection max doit provenir de votre variable de table au lieu de la table réelle

DECLARE @i int
DECLARE @PractitionerId int
DECLARE @numrows int
DECLARE @Practitioner TABLE (
    idx smallint Primary Key IDENTITY(1,1)
    , PractitionerId int
)

INSERT @Practitioner
SELECT distinct PractitionerId FROM Practitioner

SET @i = 1
SET @numrows = (SELECT COUNT(*) FROM @Practitioner)
IF @numrows > 0
    WHILE (@i <= (SELECT MAX(idx) FROM @Practitioner))
    BEGIN

        SET @PractitionerId = (SELECT PractitionerId FROM @Practitioner WHERE idx = @i)

        --Do something with Id here
        PRINT @PractitionerId

        SET @i = @i + 1
    END
12
Grax

Je dirais que tout fonctionne probablement sauf que la colonne idx n'existe pas réellement dans la table dans laquelle vous faites votre choix. Peut-être que vous vouliez choisir parmi @Practitioner:

WHILE (@i <= (SELECT MAX(idx) FROM @Practitioner))

parce que c'est défini dans le code ci-dessus comme ça:

DECLARE @Practitioner TABLE (
    idx smallint Primary Key IDENTITY(1,1)
    , PractitionerId int
)
4
Mike Perrenoud

La ligne suivante est incorrecte dans votre version:

WHILE (@i <= (SELECT MAX(idx) FROM @Practitioner))

(Manquant le @)

Peut-être une idée pour changer votre convention de nommage afin que les tables soient plus différentes.

2
Jon Egerton

Vous avez besoin du curseur SQL Server.

C'est un MSDN link. Reportez-vous this pour un exemple simple.

1
Sameer Azazi

Bien que les curseurs soient généralement considérés comme un mal horrible, j'estime qu'il s'agit d'un cas pour le curseur FAST_FORWARD - la chose la plus proche de FOREACH dans TSQL.

1
Yuriy Galanter

Cela fonctionne généralement mieux qu'un curseur (presque toujours) et est plus simple:

    DECLARE @PractitionerList TABLE(PracticionerID INT)
    DECLARE @PractitionerID INT

    INSERT @PractitionerList(PracticionerID)
    SELECT PracticionerID
    FROM Practitioner

    WHILE(1 = 1)
    BEGIN

        SET @PracticionerID = NULL
        SELECT TOP(1) @PracticionerID = PracticionerID
        FROM @PractitionerList

        IF @PracticionerID IS NULL
            BREAK

        PRINT 'DO STUFF'

        DELETE TOP(1) FROM @PractitionerList

    END
1
David Sopko

Je suis venu avec un moyen très efficace (je pense) lisible de le faire.

    1. create a temp table and put the records you want to iterate in there
    2. use WHILE @@ROWCOUNT <> 0 to do the iterating
    3. to get one row at a time do, SELECT TOP 1 <fieldnames>
        b. save the unique ID for that row in a variable
    4. Do Stuff, then delete the row from the temp table based on the ID saved at step 3b.

Voici le code. Désolé, son utilisation de mes noms de variable au lieu de ceux de la question.

            declare @tempPFRunStops TABLE (ProformaRunStopsID int,ProformaRunMasterID int, CompanyLocationID int, StopSequence int );    

        INSERT @tempPFRunStops (ProformaRunStopsID,ProformaRunMasterID, CompanyLocationID, StopSequence) 
        SELECT ProformaRunStopsID, ProformaRunMasterID, CompanyLocationID, StopSequence from ProformaRunStops 
        WHERE ProformaRunMasterID IN ( SELECT ProformaRunMasterID FROM ProformaRunMaster WHERE ProformaId = 15 )

    -- SELECT * FROM @tempPFRunStops

    WHILE @@ROWCOUNT <> 0  -- << I dont know how this works
        BEGIN
            SELECT TOP 1 * FROM @tempPFRunStops
            -- I could have put the unique ID into a variable here
            SELECT 'Ha'  -- Do Stuff
            DELETE @tempPFRunStops WHERE ProformaRunStopsID = (SELECT TOP 1 ProformaRunStopsID FROM @tempPFRunStops)
        END
0
pdschuller

Voici l'une des meilleures solutions.

DECLARE @i int
            DECLARE @curren_val int
            DECLARE @numrows int
            create table #Practitioner (idx int IDENTITY(1,1), PractitionerId int)
            INSERT INTO #Practitioner (PractitionerId) values (10),(20),(30)
            SET @i = 1
            SET @numrows = (SELECT COUNT(*) FROM #Practitioner)
            IF @numrows > 0
            WHILE (@i <= (SELECT MAX(idx) FROM #Practitioner))
            BEGIN

                SET @curren_val = (SELECT PractitionerId FROM #Practitioner WHERE idx = @i)

                --Do something with Id here
                PRINT @curren_val
                SET @i = @i + 1
            END

Ici, j'ai ajouté quelques valeurs dans la table beacuse, initialement, il est vide.

Nous pouvons accéder ou faire n'importe quoi dans le corps de la boucle et nous pouvons accéder à l'idx en le définissant dans la définition de la table.

              BEGIN
                SET @curren_val = (SELECT PractitionerId FROM #Practitioner WHERE idx = @i)

                --Do something with Id here

                PRINT @curren_val
                SET @i = @i + 1
            END
0
Joseph M