2012-10-22 13 views
5

Problem:Jak to zrobić pętlę poprzez tabeli temp w procedurze przechowywanej bez pk

muszę pętli poprzez rekordów w jednej tabeli, ciągnąc liczbę pracowników i porównywanie tej liczby pracowników w stosunku do innej tabeli sprawdź, czy nadal są aktywnymi pracownikami. Jeśli nie są już aktywnymi pracownikami, muszę przekazać dane z tego wiersza do innego zapisanego proc.

Badania:

Mam googled wokół trochę i uświadomić sobie, że nie należy używać kursorów do tego. Zrobiłem jednak znaleźć następujące przykłady:

  1. http://ask.sqlservercentral.com/questions/7969/loop-through-records-in-a-temporary-table.html
  2. http://eedle.com/2010/11/11/looping-through-records-in-sql-server-stored-procedure/

Jednak wydaje się, że użycie pk pętli ewidencji. numery pracownik może być taki sam dla wielu recods w moim scenariuszu

Pytania:

  1. to możliwe, aby osiągnąć to, co ja próbuje bez kursory?
  2. Jeśli to możliwe, w jaki sposób powinienem pobrać każdy wiersz za pomocą nieunikalnej kolumny?
+2

Nie możesz używać przyłączyć? –

+0

Nie, przechodzę przez pierwszą tabelę, porównuję do drugiej tabeli, a gdy porównanie się nie powiedzie, usuwam rekordy z pierwszej tabeli. W essense usuwam recods z pierwszej tabeli pracowników, którzy nie są już z firmą. –

+0

Jakie dane przechodzisz do procedury przechowywanej z wiersza? – podiluska

Odpowiedz

6

Ponieważ nie dałeś nam pełny opis swojej sytuacji, nie możemy dać pełnej odpowiedzi, jednak w ogóle, to Loops że chcesz uniknąć w języku ustawionym na bazie podobnego SQL i nie kursory per se (problem z Cursosr polega na tym, że wymagają pętli).

W komentarzach podajesz nieco więcej informacji, ponieważ chcesz "przechodzić przez pierwszą tabelę, porównać z drugą tabelą, a gdy porównanie się nie powiedzie, usuniemy rekordy z pierwszej tabeli. . z pierwszej tabeli pracowników, którzy nie są już w firmie "

Oto jak można to zrobić w SQL:

DELETE From FirstTable 
WHERE FirstTable.EmployeeID NOT IN 
    (
     SELECT SecondTable.EmployeeID 
     FROM SecondTable 
     WHERE SecondTable.Flag = 'Y' 
    ) 

Pętle nie są potrzebne ...


Jeśli problem jest więc, że chcesz korzystać z istniejącej wcześniej procedury przechowywanej zrobić usunięcia, a następnie istnieje kilka możliwości:

Po pierwsze, można wyodrębnić zawartość procedura składowana i re-write dla tych poprzedzających warunków WHERE. Rozumiem, że jest to powielanie kodu i że narusza instynkty DRY niektórych osób, jednak rozumiem, że SQL jest środowiskiem programistycznym zorientowanym obiektowo i że czasami musi nastąpić powielanie kodu.

Drugą opcją byłoby zmodyfikowanie procedury przechowywanej, aby mogła zaakceptować parametr TableParameter dla jego EmployeeId do usunięcia.Jest to jednak skomplikowane i musielibyśmy zapoznać się z procedurą przechowywaną, aby poinformować o tym.

Trzecią opcją byłoby użyć agregacji ciąg zbudować dynamiczny SQL, aby zadzwonić do procedury przechowywanej dla każdego IDpracownika zostać usunięty tak:

DECLARE @sql As NVarchar(MAX); 
SET  @sql = N''; 

SELECT @sql = @sql + ' 
    EXEC YourProc ''' + CAST(EmployeeID As NVARCHAR(MAX)) + '''; ' 
FROM FirstTable 
WHERE FirstTable.EmployeeID IN 
    (
     SELECT SecondTable.EmployeeID 
     FROM SecondTable 
     WHERE SecondTable.Flag = 'Y' 
    ) 

EXEC(@sql); 

ten sposób unika się zarówno pętli i problemy Cusror, choć wielu nie lubię tego również. Sam preferuję to rozwiązanie, głównie ze względu na jego ogólność.

+0

Przepraszamy za brak informacji. W drugiej tabeli znajduje się numer pracownika. Istnieje jednak kolumna z flagą, która mówi, że nie są już aktywne. Zakładam więc, że nadal będzie działać, jeśli ustawię wewnętrzne instrukcje select gdzie klauzula "WHERE isActive =" Y "". –

+0

Tak, wewnętrzna klauzula where zadziała. Odpowiem resztę w moim poście ... – RBarryYoung

+0

Czy to będzie polecenie wewnętrznego wyboru dla każdego wiersza w tabeli 1? Przyczyną, o którą pytam, jest to, że nie filtruje on zwracanych wierszy w bardzo dużym stopniu, a wewnętrzna instrukcja select zwróci dziesiątki tysięcy rekordów dla każdego rekordu w tabeli1. Nie mogę tego jeszcze przetestować, albo spróbowałbym. Ale czy mogę to zrobić w wewnętrznym select: "Where FirstTable.emp_no = SecondTable.emp_no"? –

3

Spowoduje to usunięcie wszystkich rekordów z tabeli danych pracowników, jeśli w tabeli bieżących pracowników nie ma pasujących wierszy.

ja sugest wymienić DELETE FROM z SELECT * FROM i wtedy, gdy jesteś szczęśliwy, aby usunąć wyniki zmienić go z powrotem do DELETE

DELETE FROM 
    EmployeeDataTable 
WHERE 
    NOT EXISTS 
    (SELECT 
     NULL 
    FROM 
     CurrentEmployees 
    WHERE 
     EmployeeDataTable.EmployeeID = CurrentEmployees.EmployeeID 
    ) 

EDIT: Właśnie zobaczyłem komentarz o Active flaga oznacza to, kwerendy można zmienić

DELETE FROM 
    EmployeeDataTable 
WHERE 
    EXISTS 
    (SELECT 
     NULL 
    FROM 
     CurrentEmployees 
    WHERE 
     EmployeeDataTable.EmployeeID = CurrentEmployees.EmployeeID 
     CurrentEmployees.IsActive <> 'Y' 
    ) 
+1

Kolejny dobry sposób na zrobienie tego. – RBarryYoung

+0

@RBarryYoung Dzięki za komentarz. Fakt, że pracownicy istnieją w drugiej tabeli, ale mają flagę nie euql na Y, oznacza, że ​​"NOT EXISTS" można zmienić na "EXISTS", co jest lepsze. będę edytować odpowiedź – Tobsey

+0

Dzięki, odpowiedziałeś na inne pytanie, które miałem. :) –

0
  1. byłoby to możliwe, jeśli wziął treści z sproc jesteś chce zadzwonić wewnątrz kursora i IM zawrzeć tę logikę w twoim aktualnym sproc. W tym momencie powinieneś być w stanie używać logiki opartej na zestawie. Czy sproc, który nazywacie bardzo skomplikowanym?
  2. Co powiesz na dodanie kolumny tożsamości do tabeli tymczasowej?

Czasami kursory są właściwym rozwiązaniem z różnych powodów.

2

Zrobiłbym to, przechodząc przez kursor. Możesz również dodać unikalny identyfikator do kursora, dzięki czemu wiesz, który wiersz aktualnie się znajduje.

DECLARE @id uniqueidentifier 
DECLARE @empName VARCHAR(50) 

SELECT newId() AS Id, * 
    INTO #mytemp 
    FROM MyEmployees 
    ORDER BY EmpName 

while EXISTS(SELECT TOP 1 1 FROM #mytemp) 
BEGIN 
    --Get row from cursor to process 
    SELECT TOP 1 @id = Id, @empName = EmpName FROM #mytemp 

    --Do you processing here. Call other stored proc. 

    --Remove processed row from cursor. 
    DELETE FROM #mytemp WHERE Id = @id 
END 
DROP TABLE #mytemp 
+0

Dlaczego to zostało odrzucone ?, ta pętla działa dobrze dla mnie. Muszę przyznać, że moja procedura przechowywana nie jest bardzo szybka, użyłem id do przeniesienia 45000 rekordów i zajmuje to około 4 min. Każdy rekord w moim temp. tabela powoduje 2 lub 3 polecenia aktualizacji w obszarze przetwarzania. – Michiel

1

Czy uważane za pomocą instrukcji MERGE: http://technet.microsoft.com/en-us/library/bb510625.aspx

mieć klauzule dotyczące:

  1. Jeśli dane się zgadzają między obu tabeli
  2. Gdy istnieje dane w źródle, ale nie w celu
  3. Gdy dane istnieją w miejscu docelowym, ale nie w źródle

Następnie można wstawiać, aktualizować lub usuwać rekordy w zależności od typu dopasowania. Możesz także wyprowadzać akcję z meczu do tabeli tymczasowej, aby uzyskać dodatkowe operacje niestandardowe.

Na przykład, można zrobić:

MERGE INTO Table1 
USING Table2 ON Table1.EmployeeID = Table2.EmployeeID 
WHEN MATCHED 
    THEN UPDATE SET Table1.SomeField = Table2.SomeOtherField 
WHEN NOT MATCHED BY SOURCE 
    THEN DELETE 
WHEN NOT MATCHED BY TARGET 
    THEN Insert (Name, Status) VALUES (EmployeeName, 'Active') 
+0

Muszę tylko usunąć, a nie wstawić lub zaktualizować. Również używam serwera sql 2005 :) –

+0

Ok. Powinieneś podać wersję SQL Server, której używasz w swoim pytaniu, jeśli nie używasz najnowszej wersji, lub przynajmniej takiej, która nie jest nieaktualna od 5 lat ;-) –

+0

Kod wygląda całkiem prosto. Niestety nie mogę uruchomić go na MS SQL Server 2008 R2. Które wersje SQL Server obsługują tę składnię? – Michiel

Powiązane problemy