Łączenie wierszy

0

Witam wszystkich.

Posiadam bazę w MS SQL 2008, gdzie do jednej z tabeli wpada co jakiś czas 151 wierszy jednocześnie(jest to odczyt wartości ze sterownika). Poszukuję optymalnego rozwiązania aby te dane przekształcić w trochę inna postać, a dokładnie chciał by aby dane z tej tabeli:
np.:
wiersz : wartość
1: 4
2: 24
3: 145
4: 232
5: 445
6: 1
7: 5
8: 34
9: 52
10: 42
11: 43
12: 1
13: 0
14: 0
15: 0
16: 0
17: 0
18: 0
.
.
......
Zmienić w taką postać
wiersz : wartość
1: 4/24/145/232/445
2: 1
3: 5/34/52/42/43
4: 1
5: 0/0/0/0/0
6: 0

Czyli 5 pierwszych wierszy łączymy, następnie wartość z szóstego, kolejne 5 łączymy i wartość z kolejnego itd.
Może ma ktoś jakiś pomysł jak to optymalnie rozwiązać, zależy mi na bardzo efektywnym rozwiązaniu ponieważ chciał bym aby to działało w triggerze a takie bloki danych po 151 wierszy, wstawiane są nawet co 0,5 sekundy

0

kolumna wiersz jest zawze numerowana od 1 do 151?

0

Nie zawsze, nie raz jest tych wierszy 115, 121

0

A możesz po prostu zrezygnować z bazy w tym przypadku albo od razu wstawiać "dobrze"?

0

Z bazy nie mogę zrezygnować.
A innej możliwości wstawiania tych danych nie ma - tak jest oprogramowany sterownik i tylko w blokach danych wysyłane są wartości z rejestrów, które następnie serwer sterownika wstawia do BD.

1

moja propozycja jest taka

  1. podziel wyniki na 6 (na 6 bo zawsze interesuje Cię 6 kolejnych rekordów) żebyś wiedział ile masz grup (bo jak piszesz nie zawsze wpada Ci jednakowa liczba wierszy)
  2. zastosuj dwie pętle while: "zewnętrzna" będzie Ci pilnowała która grupa aktualnie jest obrabiana, "wewnętrzna" która pozycja w grupie jest aktualnie obrabiana

nie sprawdzałem tego kodu ale myślę, że nawet jeśli gdzieś wdarł się jakiś błąd to go łatwo skorygujesz chodzi o zasadę :)

-- tabelę źrółową nazwijmy po prostu tabela
declare @ile_razy = (SELECT COUNT(*)/6 FROM tabela); -- tutaj masz liczbę powtórzeń które trzeba wykonać pętlą
declare @i int = 0;
dedlare @j int =1;
while @i < @ile_razy  -- @i wskazuje Ci którą grupa teraz jest obrabiana
	begin
		while @j < 7  --@j wskazuje Ci które powtórzenie w grupie jest obrabiane
    			begin
      				declara @id_start int = 6*@ile_grup; -- teraz masz id od którego startujesz za każdym razem przechodzisz do grupy wyżej
				declare @w1 int = (SELECT wartosc FROM tabela WHERE wiersz = @i*6+1);
				declare @w2 int = (SELECT wartosc FROM tabela WHERE wiersz = @i*6+2);
				declare @w3 int = (SELECT wartosc FROM tabela WHERE wiersz = @i*6+3);
				declare @w4 int = (SELECT wartosc FROM tabela WHERE wiersz = @i*6+4);
				declare @w5 int = (SELECT wartosc FROM tabela WHERE wiersz = @i*6+5);
				declare @w6 int = (SELECT wartosc FROM tabela WHERE wiersz = @i*6+6);
				-- Tutaj wstawiasz INSERT INTO do nowej tabeli w sposób jaki chcesz
				-- pamiętaj o konwersji typów bo jeżeli chcesz łączyć kilka wartości oddzielonych separatorem
				-- to będziesz to pewnie trzymał w stringu a wartości @W1-@W6 masz jako liczby
				set @j +=1 
   			 end
		set @i +=1
	end

jeśli pomogłem kliknij na kciuka do góry, że wpis jest wartościowy :)

0

Wielkie dzięki, właśnie o to mi chodziło.
W sumie druga pętla jest już nie potrzebna :)

0

Można to zrobić jednym prostym zapytaniem z użyciem zmiennej @lp=case when @lp<6 then @lp+1 else 1 end
ale niestety z braku MS SQL 2008 nie napiszę tego i nie sprawdzę. Przy wielu danych kod podany prze4z @KiK może się nie wyrobić w 0.5 s

0

Jeżeli będziesz to wykonywał co 0,5 sekundy, to jakby to było optymalne może powodować zecięcia.
Masz ode mnie 2 rozwiazania dla 151 rekordów #1 działa mniej niż sekundę, dla 100 000 nie wiem ale po 3 minutach zatrzymałem

#1

with cteW as (
	select 
		(wiersz / 6) g1
		,case 
			when (wiersz / 6) > 0 then 
				case when row_number() over (partition by (wiersz / 6) order by wiersz)=1 then 
					0 
				else 
					1 
				end 
			else 
				0 
			end g2
		, wartość 
	from 
		tabela
)

select
	STUFF(
			(
			SELECT 
				N'/' + CAST([WARTOŚĆ] AS VARCHAR(255))
			FROM 
				cteW as c
			WHERE 
				c.g1=dt.g1 
				and c.g2=dt.g2
			FOR XML PATH ('')
			)
			, 1
			, 1
			, ''
		)
FROM (
	select distinct 
		g1
		,g2
	from 
		cteW) DT

#2
Rozwiazanie 2 to wykorzystanie CLR do napisania funkcji grupującej
(albo użyć istniejącej)

i wtedy skrypt:

with cteW as (
	select 
		(wiersz / 6) g1
		,case 
			when (wiersz / 6) > 0 then 
				case when row_number() over (partition by (wiersz / 6) order by wiersz)=1 then 
					0 
				else 
					1 
				end 
			else 
				0 
			end g2
		, wartość 
	from 
		michal.dbo.tabela
)

select
	g1
	,g2
	,dbo.GROUP_CONCAT_D(wartość,N'/')
from
	cteW
group by
	g1
	,g2
order by
	g1
	,g2

dla 100 000 trwa około sekundy

P.S.
Jest jeszcze opcja 3, tylko dla najnowszego SQL Servera funkcja: string_agg https://docs.microsoft.com/en-us/sql/t-sql/functions/string-agg-transact-sql

1 użytkowników online, w tym zalogowanych: 0, gości: 1