2012-08-23 6 views
5

Mam tabelę do aktualizacji przy użyciu rekordów innej tabeli i robię to, aby przenieść informacje z jednego systemu (bazy danych) do innego. Scenariusz jest kompleks nieco, ale rozpaczliwie potrzebują pomocy :-sSQL INNER QUERY zwraca więcej niż jedną wartość w zapytaniu UPDATE

Są 3 stoły - component, scan i stage_link

elementów

component_id stage_id 
------------ -------- 
1    NULL 
2    NULL 
3    NULL 
4    NULL 
5    NULL 

skanowania

scan_id component_id scanner_id   date_scanned 
------- ------------ ---------- ----------------------- 
1   1   scanner_a 2012-01-01 07:25:15.125 
2   1   scanner_b 2012-01-02 08:14:05.456 
3   2   scanner_a 2012-01-01 12:05:45.465 
4   3   scanner_a 2012-01-01 19:45:12.536 
5   1   scanner_c 2012-01-03 23:33:54.243 
6   2   scanner_b 2012-01-02 11:59:12.545 

stage_link

stage_link_id scanner_id stage_id 
    -------  ---------- ---------- 
     1   scanner_a 1 
     2   scanner_b 1  
     3   scanner_c 2  
     4   scanner_d 2  
     5   scanner_e 2 
     6   scanner_f 3 

muszę update ze stołu component i set dziedzinie stage_id według ostatniego skanowania. Każdy skan przenosi komponent na scenę zgodnie ze skanerem. Napisałem następującą kwerendę, aby update tabeli component, ale rzuca błąd mówiąc;

Subquery returned more than 1 value. This is not permitted when the subquery follows '='

Zapytanie jest;

UPDATE component 
SET stage_id = (select stage_id 
       from(
        select scn.scanner_id, sl.stage_id 
        from scan scn 
        INNER JOIN stage_link sl ON scn.scanner_id = sl.scanner_id 
        where scn.date_scanned = ( select temp_a.max_date 
               from ( SELECT x.component_id, MAX(x.date_scanned) as max_date 
                 FROM scan x 
                 where component_id = x.component_id 
                 GROUP BY x.component_id 
                ) as temp_a 
               where component_id = temp_a.component_id) 
        ) as temp_b 
       ) 

pracuję nad MS SQL Server i chce to rozwiązać za pomocą żadnej PHP lub jakiegokolwiek innego języka.

Próbowałem poświęcić jeden dzień na wykonanie tej pracy, ale nadal nie udało mi się tego dokonać. Każda pomoc będzie wysoko ceniona!

Dziękuję bardzo z góry :-)

Odpowiedz

4

Sprawdź to bez użycia skorelowanych podkwerend:

UPDATE Com 
SET  stage_id = Temp4.stage_id 
FROM dbo.component Com 
     INNER JOIN 
     ( 
      SELECT Temp2.component_id ,SL.stage_id 
      FROM dbo.stage_link SL 
      INNER JOIN (
          SELECT component_id ,scanner_id 
          FROM scan 
          WHERE date_scanned IN (
           SELECT MaxScanDate 
           FROM  
           ( 
            SELECT component_id , MAX(date_scanned) MaxScanDate 
            FROM scan 
            GROUP BY component_id 
           ) Temp 
          ) 
         ) Temp2 ON Temp2.scanner_id = SL.scanner_id 
     ) Temp4 ON Com.component_id = Temp4.component_id 

Wyjście:

component_id stage_id 
------------ ----------- 
1   2 
2   1 
3   1 
4   NULL 
5   NULL 
+0

Dziękuję bardzo za tę sugestię !! to przyniosło wielką pomoc !! Właśnie tego chciałem !!! :-) jeszcze raz, wielkie dzięki!!!!! – mithilatw

+0

@mithilatw: Nie ma za co. Należy pamiętać, że mogą wystąpić 2 problemy z tym zapytaniem (w celu późniejszego wykorzystania): (1) Zakłada się, że data_scanned jest zawsze unikalna. Jeśli skanowane są 2 skanery w tym samym czasie (do połowy nanosekundy), wówczas SELECT id_komponentu, scanner_id' zwróci 2 wartości. (2) Pola w łączeniu 'Temp2.scanner_id = SL.scanner_id' są kolumnami opartymi na znakach, więc jeśli masz wiele skanerów, zapytanie to może zacząć zwalniać. – Kash

2

dobrze, podzapytanie zwraca więcej niż jedną wartość. Prostym sposobem jest wykonanie agregacji:

SET stage_id = (select max(stage_id) 
. . . 

Prawdopodobną przyczyną jest to, że w najnowszej dacie jest więcej niż jedno skanowanie. Ponieważ możesz wybrać tylko jeden, biorąc pod uwagę kontekst, wystarczy MIN lub MAX.

Jednak moim zdaniem prawdziwym powodem jest to, że nie masz odpowiednich aliasów do skorelowanych podkwerend. Myślę, że te linie:

where component_id = x.component_id 
where component_id = temp_a.component_id 

Powinien zawierać aliasy, prawdopodobnie będąc:

where component.component_id = x.component_id 
where component.component_id = temp_a.component_id 

Jeśli to nie wystarczy, trzeba wyjaśnić, co chcesz. Czy chcesz, aby zapytanie zwróciło losowy skan od najnowszej daty? Czy chcesz zaktualizować komponent dla wszystkich skanów w najnowszej dacie?

Musisz zbadać to dokładniej.Spróbuj czegoś takiego:

select scn.scanner_id, sl.stage_id, count(*) 
from scan scn INNER JOIN 
    stage_link sl 
    ON scn.scanner_id = sl.scanner_id join 
    (SELECT x.component_id, 
      MAX(x.date_scanned) as max_date 
     FROM scan x 
     GROUP BY x.component_id 
    ) cmax 
    on scn.component_id = cmax.component_id 
where scn.date_scanned = cmax.maxdate 
group by scn.scanner_id, sl.stage_id 
order by count(*) desc 
+0

Dziękuję bardzo za odpowiedź !! Próbuję zaktualizować wszystkie składniki tabeli "składnik", patrząc na skany, które są wykonywane na jednym komponencie na raz. Dlatego może istnieć tylko jedno skanowanie w dniu 'max', kiedy komponent' zgrupowany przez'. Problem z Twoją sugestią to dokładność informacji. Maksymalna wartość liczbowa "stage_id" nie jest ostatnim etapem, w którym komponent mógłby być (przykro, że ten scenariusz jest tak skomplikowany!). Moim problemem jest to, że może być tylko jeden skan w dniu 'max' na składnik i nadal zwraca więcej niż 1 :-( – mithilatw

+1

@mithilatw. Weź podkwerendę z aktualizacji i rozpocznij sprawdzanie, gdzie występuje zduplikowany. Może to być duplikat w jednej z tabel według parametru scanner_id lub component .Doświadczenie sugeruje jednak, że duplikaty w tym samym dniu jest bardzo prawdopodobnym winowajcą –

+0

Próbowałem to, nie działa.Nie powinno być lepiej niż przy użyciu skorelowanych podkwerend – Kash

2

to potrzebne funkcję OLAP pracować:

UPDATE Component SET Component.stage_id = Stage_Link.stage_id 
FROM Component 
JOIN (SELECT component_id, scanner_id, 
      ROW_NUMBER() OVER(PARTITION BY component_id 
           ORDER BY date_scanned DESC) rownum 
     FROM Scan) Scan 
    ON Scan.component_id = Component.component_id 
    AND Scan.rownum = 1 
JOIN Stage_Link 
    ON Stage_Link.scanner_id = Scan.scanner_id 
WHERE Component.stage_id IS NULL 

który generuje zestaw wyników:

Component 
component_id stage_id 
======================== 
1    2 
2    1 
3    1 
4    null 
5    null 

(Mam też pracę SQL Fiddle example .)

+0

Dziękuję bardzo za poświęcony czas! Próbowałem tej sugestii w mojej bazie danych i zapytanie trwało ponad 1 godzinę, ale nadal nie zwracało wyniku. Widzę pomysł, który próbujesz wdrożyć i zgadzam się z nim. Nie rozumiem, dlaczego moja baza danych woul nie akceptuj tego :-P – mithilatw

+0

# winces # Właściwie twój problem polega na tym, że twoja baza danych _did_ akceptuje to - po prostu nie działa tak szybko, jak to tylko możliwe. To _prawdopodobnie_ uruchamia pobieranie wartości dla każdej linii. Nie jest to problemem, gdy jest mały (jak na dane testowe), ale oczywiście nie skaluje się dobrze. Czy SQL Server nie obsługuje CTE w instrukcjach 'UPDATE'? Możesz próbować uzyskać przynajmniej część z 'ROW_NUMBER()' na CTE, tak aby była uruchamiana tylko raz (to jest prawdopodobnie najlepsze oszczędności). Ewentualnie zrzuć wyniki podselekcji do tabeli tymczasowej i uruchom z niej skorelowaną aktualizację. –

+0

@ X-Zero: Myślę, że chodziło o skorelowane podkwerendy zamiast CTE. SQL Server obsługuje oba, ale ze skorelowanymi podzapytaniami, jak wspomniano, ocenia dla każdego wiersza Komponentu, a zatem nie jest dokładnie oparty na zestawie, co wyjaśniałoby, dlaczego zajmuje to dużo czasu. – Kash