webForumDet fria alternativet

Prestandafråga...

9 svar · 692 visningar · startad av nakoz

nakozMedlem sedan dec. 1999653 inlägg
#1

Hej!

Jag har en fråga gällande prestanda.
För lite mer än ett år sedan utvecklade jag ett onlineverktyg som har skjutit i användande och databasen är betydligt större och sidorna "tyngre" än jag räknat med då jag konstruerade det.

Det går ut på att göra "paket" med innehåll bestående av 16 bilder. Sen består varje "order" av 4-15 paket. 15 paket X 16 bilder = 240 bilder.

Nuvarande lösning

Tabeller:
order
oInnehall
bilder

En order får ett nummer, namn och ett id i order.
Innehållet i ordern läggs i oInnehall.
Alltså kan en order ha 240 rader i oInnehall, där varje rad innehåller "placering" i paketet samt i vilket paket, typ: paket 3, plats 12 och bilden sedan hämtas med ett id från bilder.
Placeringen är otroligt viktig, men det blir sirap med 240 rader som skall hämtas från databasen.

Nytt förslag

Nu tänkte jag göra om strukturen på detta, så att - istället för att varje bild får en rad i oInnehall så får varje paket ett innehåll i form av en array med 16 värden som motsvarar bildID't som senare hämtas från bilder. En rad i databasen ser således ut såhär: 3,5,,2,6,8,,,4,15,14,1,9,11,,, och bör generera:

_________________
| 3 | 5 | X | 2 |
| 6 | 8 | X | X |
| 4 | 15| 14| 1 |
| 9 | 11| X | X |

Typ.

Nu till frågan;
Tänker jag rätt?
Finns det ännu bättre sätt att utföra det jag efterfrågar?

Jättetacksam för svar.

spangoMedlem sedan juni 20008 205 inlägg
#2

Kommaseparerade värden är oftast inte en helt mysig lösning. Hur ser dina index och SQL-frågor ut? Tycker inte att det borde vara så segt.

jonneMedlem sedan jan. 20011 154 inlägg
#3

Låter som om problemet ligger mer i utförandet än lagringen. En databasserver har inga problem att hantera den mängden data du beskriver på det sättet. Så som Spango skriver så är nog indexering och sqlfrågor ett bättre ställe att börja på för att lagra oderdata på det där sättet kommer att ge dig en hel uppsjö av nya problem :)

LarsGMedlem sedan dec. 200012 464 inlägg
#4

Flyttas från ASP

nakozMedlem sedan dec. 1999653 inlägg
#5

Jag beskrev det kanske lite väl enkelt.

På varje rad i ett 4x4-paket så räknas ett snittpris ut per rad.
På varje paket så räknas ett snittpris ut på hela paketet.
Oavsett om det är tomt på en rad eller fyra bilder.
Bilderna drar även sitt strå till stacken (240x4kb) att det är en halvslö lösning idag.

Jag bidrar med lite SQL:
Ett paket:

Response.Write "<table cellpadding='0' cellspacing='0' border='0'><tr><td valign='top'>"

For i = 1 To paket 
	Response.Write "<center><b>paket " & i & "</b>"	
	Response.Write "</center><table cellpadding='0' cellspacing='1' width='216'><tr width='216'>"

	For o = 1 To 16
		If o = 1 OR o = 3 OR o = 6 OR o = 8 OR o = 9 OR o = 11 OR o = 14 OR o = 16 Then strCol = "#F1F1F1" Else strCol = "#F7F7F7"

		Response.Write "<a href=""inl_planAdd.asp?se=" & i & "&ru=" & o & """ target=""planoleft"">"
		Response.Write "<td bgcolor='" & strCol & "' valign='top' width='54' height='54'>"

If Session("tillOrder") <> "" Then		
		SQLx = "SELECT * FROM oInnehall WHERE plTillOrder = " & Session("tillOrder") & " AND plpaket = " & i & " AND plRuta = " & o
		Set rsx = cn.Execute(SQLx)
	
		If NOT rsx.EOF Then
			SQLy = "SELECT * FROM bilder WHERE pID = " & rsx("plProdukt")
			Set rsy = cn.Execute(SQLy)

			If rsy.EOF Then
				Response.Write rsx("plProdukt")
			Else
				Response.Write "<a href=""inl_planAdd.asp?se=" & i & "&ru=" & o & """ target=""planoleft"">"
				Response.Write "<img src=""img/bild/art" & rsy("pArtNr") & ".jpg"" border=""0"" width=""54"" height=""54"" alt=""" & rsy("pNamn") & """ onerror=""this.src='img/bild/bild_saknas.jpg'""></a>"
			End If
		End If

		rsx.Close
End If

		Response.Write "</td></a>"
	If o Mod 4 = 0 Then Response.Write "</tr><tr>"
Next

rsy.Close

Response.Write "</tr></table>"

So far so good alltså.

Här kommer de lite elaka uträkningarna... Kanske här jag har "fucked up" med koden... :O

If Session("tillOrder") <> "" Then
	Set rsP = cn.Execute("SELECT * FROM oInnehall WHERE plTillOrder = " & Session("tillOrder") & " AND plPaket = " & i & "")
	If NOT rsP.EOF Then 
	Set rsA = cn.Execute("SELECT COUNT(plID) AS Antal FROM oInnehall WHERE plTillOrder = " & Session("tillOrder") & " AND plPaket = " & i & "")
If NOT rsA.EOF Then
		Do Until rsP.EOF
			Set rsP2 = cn.Execute("SELECT * FROM bilder WHERE pID = " & rsP("plProdukt"))
			If NOT rsP2.EOF Then priz = priz + cDbl(rsP2("pInkop"))
			rsP.MoveNext
		Loop
End If 
		priz = Round(cDbl(priz)/cDbl(rsA("Antal")),2)
	End If
	Response.Write "<center><div class='tdsmallny'><b>" & priz & "</b></div></center>"
	rsP.Close
End If

	Response.Write "</td><td width='35'>"

	Response.Write "<table cellpadding='1' cellspacing='0' width='35'>"
	Response.Write "<tr height='54'><td class='tdsmallny'>"

If Session("tillPlan") <> "" Then
	Set rsP = cn.Execute("SELECT * FROM plcontent WHERE plTillOrder = " & Session("tillOrder") & " AND plSektion = " & i & " AND plRuta >= 0 AND plRuta < 5")
	If NOT rsP.EOF Then 
	Set rsA = cn.Execute("SELECT COUNT(plID) AS Antal FROM plcontent WHERE plTillOrder = " & Session("tillOrder") & " AND plSektion = " & i & " AND plRuta >= 0 AND plRuta < 5")
		Do Until rsP.EOF
			Set rsP2 = cn.Execute("SELECT * FROM bilder WHERE pID = " & rsP("plProdukt"))
			znitt1 = cInt(znitt1) + cDbl(rsP2("pInkop"))
			rsP.MoveNext
		Loop

	znitt1 = Round(cInt(znitt1)/cInt(rsA("Antal")),2)

	rsP.Close
	rsP2.Close
	rsA.Close

	End If

Response.Write znitt1
End If
spangoMedlem sedan juni 20008 205 inlägg
#6

Det är asjobbigt att läsa SQL inbakat i ASP-kod på det här viset (plocka gärna ut frågorna och skriv vad de är tänkta att göra nästa gång :) ), men är följande kolumner indexerade:

  • oInnehall.plTillOrder
  • bilder.pID
  • plcontent.plTillOrder

Är de inte det, och du har många rader, vore det inte konstigt om det gick segt. Om du försöker köra frågorna i något adminverktyg för din databas, hur lång tid tar de då? Vad har du för databas?

OveRRidEMedlem sedan feb. 200112 078 inlägg
#7

För det första; du ska absolut inte byta till att lagra värden kommaseparerade i databasen. Det om något kommer ge dig prestandaproblem och för att inte tala om problem att uppdatera/ändra data eller ha en läsbar kod överhuvudtaget. Som många sagt tidigare; en databas är byggd för att hantera data radvis och dina mängder är inga som helst problem. Indexering är en viktig faktor, precis som spango säger.

Men för guds skull, inte kommaseparerad data. ;)

nakozMedlem sedan dec. 1999653 inlägg
#8

Okej.

Då är det mest en vässning av mina befintliga SQL-satser jag behöver hjälp med.

Jag har "filtrerat" (läs: handjagat :) ) bort all html, för det kanske inte uteslutande är mina sql-frågor som suger, utan det kan även vara annat...

Jag har även lagt in lite förklarande kommentarer.

Min DBMS är f.n MySQL och kanske att uträkningarna går att göra smidigare (då frågorna skrevs för en access som verktyget initialt kördes i).

Indexeringsfält måste väl vara unika, eller har jag missuppfattat?
plTillOrder är ju inte unik, utan har ~100-250 poster med samma värde?
Jag är dock ingen stjärna på index och har försökt söka lite utan att direkt få någon annan förklaring?

Tacksam för all tidigare hjälp och eventuellt kommande? :)

Här listas kod för ett paket -> en snittuträkning/paket -> EN snittuträkning/rad (det finns 4 rader).

[1]For i = 1 To paket 			' ** Vi väljer hur många paket som ska skapas
	For o = 1 To 16			' ** Sen ska för varje paket 16 rutor skapas

		If o = 1 OR o = 3 OR o = 6 OR o = 8 OR o = 9 OR o = 11 OR o = 14 OR o = 16 Then strCol = "#F1F1F1" Else strCol = "#F7F7F7"

		SQLx = "SELECT * FROM oInnehall WHERE plTillOrder = " & Session("tillOrder") & " AND plpaket = " & i & " AND plRuta = " & o
		Set rsx = cn.Execute(SQLx)
	
		If NOT rsx.EOF Then
			SQLy = "SELECT * FROM bilder WHERE pID = " & rsx("plProdukt")
			Set rsy = cn.Execute(SQLy)

			If rsy.EOF Then
				' ** FEL - BILD FINNS EJ
			Else
				' ** VISA BILD
			End If
		End If

	If o Mod 4 = 0 Then Response.Write "ny rad"

	Next

' ** NEXT #2 STÄNGS LÄNGRE NER, UNDER ALLA UTRÄKNINGAR.

znitt1 = 0
priz = 0

' ** HÄMTAR ALLT INNEHÅLL I UTVALT PAKET
Set rsP = cn.Execute("SELECT * FROM oInnehall WHERE pltillOrder = " & Session("tillOrder") & " AND plpaket = " & i & "") 

If NOT rsP.EOF Then 
	' ** RÄKNAR ANTAL POSTER I ANGIVET PAKET
	Set rsA = cn.Execute("SELECT COUNT(plID) AS Antal FROM oInnehall WHERE pltillOrder = " & Session("tillOrder") & " AND plpaket = " & i & "")

	If NOT rsA.EOF Then
		Do Until rsP.EOF

			' ** HÄMTAR "BILDENS" PRIS
			Set rsP2 = cn.Execute("SELECT * FROM bilder WHERE pID = " & rsP("plProdukt"))
			If NOT rsP2.EOF Then priz = priz + cDbl(rsP2("pInkop"))
			rsP.MoveNext
		Loop
	End If 

	' ** RÄKNAR UT ETT SNITT EFTER HUR MÅNGA RUTOR I HELA PAKETET SOM ÄR FYLLDA
	priz = Round(cDbl(priz)/cDbl(rsA("Antal")),2)
End If

	Response.Write priz
End If

' ** HÄMTAR RUTA 1-4 FÖR ATT RÄKNA SNITT PÅ VALD RAD
Set rsP = cn.Execute("SELECT * FROM oInnehall WHERE pltillOrder = " & Session("tillOrder") & " AND plpaket = " & i & " AND plRuta >= 0 AND plRuta < 5")

If NOT rsP.EOF Then 
	' ** RÄKNAR UT ANTAL FYLLDA RUTOR I RADEN
	Set rsA = cn.Execute("SELECT COUNT(plID) AS Antal FROM oInnehall WHERE pltillOrder = " & Session("tillOrder") & " AND plpaket = " & i & " AND plRuta >= 0 AND plRuta < 5")
	Do Until rsP.EOF
		Set rsP2 = cn.Execute("SELECT * FROM bilder WHERE pID = " & rsP("plProdukt"))
		znitt1 = cInt(znitt1) + cDbl(rsP2("pInkop"))
		rsP.MoveNext
	Loop

' ** RÄKNAR UT ETT SNITT FÖR RADEN EFTER HUR MÅNGA RUTOR SOM ÄR FYLLDA
znitt1 = Round(cInt(znitt1)/cInt(rsA("Antal")),2)

End If

Response.Write znitt1[/1]
spangoMedlem sedan juni 20008 205 inlägg
#9

Nej, indexerade fält behöver inte vara unika (men vill man ha unika fält ska de vara indexerade). Index är mer som en sorterad lista över värden i en kolumn (eller flera) med pekare tillbaka på de raderna värden ligger i, och det är mycket snabbare att söka i en sorterad lista.

Sen är det kanske inte alltid överlägset bäst att köra SQL-frågor i loopar, ibland kan det vara bättre att plocka ut alla värden på en gång och spara de i någon slags mellanliggande datastruktur, men innan du börjar skriva om din kod tycker jag att du ska se till att dina index finns där de ska vara. Skillnaden mellan en indexerad sökning och en icke-indexerad sökning är som natt och dag om det börjar bli ett gäng rader, och skillnaden blir större ju fler rader som finns i tabellen.

Som sagt, kör dina SQL-frågor i en klient direkt mot databasen och se hur lång tid de tar. Eftersom du kör MySQL kanske du även vill köra EXPLAIN <frågan> för att se hur den optimerar. Kolla:
http://www.databasejournal.com/features/mysql/article.php/1382791
http://dev.mysql.com/doc/refman/5.0/en/explain.html

nakozMedlem sedan dec. 1999653 inlägg
#10

Jag indexerade några av fälten och fick en viss förbättring i prestandan, men i mina ögon inte tillräcklig.

Efter en del kliande i huvudet så lyckades jag skriva om några frågor till en och det gjorde underverk med mina uträkningar av snitt.

Jag postar dem och markerar tråden som löst. Tack för all input dock!

SELECT Sum(prod.pInkop)/COUNT(cont.plID) AS Inkop 
FROM oInnehall cont, bilder prod 
WHERE cont.plTillOrder = " & Session("tillOrder") & " 
AND prod.pID = cont.plProdukt 
AND cont.plPaket = " & i & " 
GROUP BY cont.plSektion
SELECT Sum(prod.pInkop)/COUNT(cont.plID) AS Inkop 
FROM oInnehall cont, bilder prod 
WHERE cont.plTillOrder = " & Session("tillOrder") & " 
AND prod.pID = cont.plProdukt 
AND cont.plPaket = " & i & " 
AND cont.plRuta BETWEEN " & rutaX & " AND  " & rutaY & " 
GROUP BY cont.plSektion
137 ms totalt · 3 externa anrop · v20260731065814-full.30151723
0 ms — hämta forumlista (cache)
0 ms — hämta statistik (cache)
134 ms — hämta tråd, inlägg och bilagor (db)