2009-03-17 12 views
37

Idealnie chcę, aby to zrobić:Jak zaktualizować i porządek za pomocą MS SQL

UPDATE TOP (10) messages SET status=10 WHERE status=0 ORDER BY priority DESC; 

W języku angielskim: Chcę otrzymać 10 najlepszych dostępnych (status = 0), wiadomości z DB i zablokować je (status = 10). Wiadomość o wyższym priorytecie powinna zostać odebrana jako pierwsza.

Niestety, MS SQL nie zezwala na klauzulę order by w aktualizacji.

W każdym razie, jak tego uniknąć?

Odpowiedz

37

Można zrobić podkwerenda gdzie najpierw uzyskać identyfikatory z 10 zamówionych przez priorytetu, a następnie zaktualizować te, które są na tej kwerendzie sub:

UPDATE messages 
SET status=10 
WHERE ID in (SELECT TOP (10) Id 
      FROM Table 
      WHERE status=0 
      ORDER BY priority DESC); 
+0

biorąc pod uwagę, że chciałbym indeks dla tego rozwiązania. Czy będę wtedy używał: (priorytetowy opis, status) lub (status, priorytetowy opis)? Innymi słowy: czy zamówienie jest używane przed miejscem? – Toad

+6

Faktycznie zapytanie nie było poprawne .... klauzula order by nie może być używana w podzapytaniu, O ile podane jest również TOP. (Tak mówi silnik SQL). Tak więc prawidłowe zapytanie powinno brzmieć: UPDATE messages SET status = 10 WHERE ID in (SELECT TOP (10) Id Z tabeli WHERE status = 0 ORDER BY priorytet DESC); – Toad

+0

Masz rację, przegapiłem szczyt, kiedy napisałem ostatnią zmianę. Będę edytować, jak powiedziałeś –

84
WITH q AS 
     (
     SELECT TOP 10 * 
     FROM messages 
     WHERE status = 0 
     ORDER BY 
       priority DESC 
     ) 
UPDATE q 
SET  status = 10 
+2

+1 dla CTE, robiąc to w ten sposób pozbywając się sprzężenia dla mnie, gdy używasz klauzuli OUTPUT – nitzmahone

+4

To byłaby faktycznie preferowana odpowiedź, a nie akceptowana. – Carvellis

+0

Należy pamiętać, że te dwa stwierdzenia nie są atomowe. – Haroon

0

Jak podano w komentarzach poniżej, można również użyć klauzuli SET ROWCOUNT, ale tylko dla SQL Server 2014 i starszych.

SET ROWCOUNT 10 

UPDATE messages 
SET status = 10 
WHERE status = 0 

SET ROWCOUNT 0 

Więcej informacji: http://msdn.microsoft.com/en-us/library/ms188774.aspx

lub z tabeli temp

DECLARE @t TABLE (id INT) 
INSERT @t (id) 
SELECT TOP 10 id 
FROM messages 
WHERE status = 0 
ORDER BY priority DESC 

UPDATE messages 
SET status = 10 
WHERE id IN (SELECT id FROM @t) 
+4

Dla tych, którzy czytają to daleko wstecz .. (nigdy nie wiadomo.) SET ROWCOUNT czeka na wycofanie http: // msdn.microsoft.com/en-us/library/ms188774.aspx "Użycie SET ROWCOUNT nie wpłynie na instrukcje DELETE, INSERT i UPDATE w przyszłej wersji SQL Server." - przynajmniej do SQL Server 2014. –

+0

Podczas gdy 'rowcount' działa dobrze, jeśli chcesz mieć dowolne 10 wierszy, nie możesz określić' order by', aby dokładnie określić, który 10. Twój przykład z tabelą tymczasową działa, ale opiera się na kolumnie id. –

1
UPDATE messages SET 
status=10 
WHERE ID in (SELECT TOP (10) Id FROM Table WHERE status=0 ORDER BY priority DESC); 
2

mam do zaoferowania to jako lepszego podejścia - nie zawsze mają luksus pole tożsamości:

UPDATE m 
SET [status]=10 
FROM (
    Select TOP (10) * 
    FROM messages 
    WHERE [status]=0 
    ORDER BY [priority] DESC 
) m 

Możesz również uczynić zapytanie dodatkowe tak skomplikowanym, jak chcesz - dołączanie wielu tabel itp ...

Dlaczego jest lepiej? Nie polega na obecności pola tożsamości (lub innej unikalnej kolumny) w tabeli messages. Może być użyty do aktualizacji górnych N wierszy z dowolnej tabeli, nawet jeśli ta tabela nie ma w ogóle żadnego unikalnego klucza.

+0

Jak ta odpowiedź różni się od odpowiedzi dotjoe: http://stackoverflow.com/a/655561/2279200 – Athafoud

+1

Nie jestem zaskoczony przez głosowanie w dół na moją odpowiedź, jeśli nie poświęca się czasu, aby spróbować to zrozumieć. Moja odpowiedź różni się od odpowiedzi dotjoe - i większości innych odpowiedzi tutaj - ponieważ pozostałe odpowiedzi zakładają, że tabela ma pole tożsamości (... gdzie ID IN ...). Nie zawsze można założyć, że tabela będzie miała pole tożsamości. Zaproponowałem alternatywę, gdy nie masz pola identyfikacyjnego w swoim stole. Spróbuj zrozumieć odpowiedź przed głosowaniem w dół. – mfascino

+0

Dziękuję za krótkie wyjaśnienie. Poświęć trochę czasu, aby edytować swoją odpowiedź i dodać szczegóły, o których właśnie wspomniałeś. Sprawi, że odpowiedź będzie pełniejsza i łatwiejsza do zrozumienia bez poświęcania jej zbyt wiele czasu. Po edycji odpowiedzi odwołam mój głos. – Athafoud

Powiązane problemy