/ Forside / Teknologi / Udvikling / SQL / Nyhedsindlæg
Login
Glemt dit kodeord?
Brugernavn

Kodeord


Reklame
Top 10 brugere
SQL
#NavnPoint
pmbruun 1704
niller 962
fehaar 730
Interkril.. 701
ellebye 510
pawel 510
rpje 405
pete 350
gibson 320
10  smorch 260
Et meget tricky SQL-problem (MySQL)
Fra : Martin Larsen


Dato : 29-04-09 22:16

Hej alle

I forbindelse med en opgave hvor en applikation skal tilpasses en helt
ny tabelstruktur med et meget stort konverteringsarbejde til følge, er
jeg kommet frem til at den bedste og hurtigste løsning er at lave et
view i den nye DB som "emulerer" den gamle struktur. Hvis dette er
muligt, skal der kun rettes minimalt i applikationen.

Min første tanke var at det måtte være muligt, men efter en masse
overvejelser og eksperimenter er jeg knap så sikker!

Nedenstående stærkt simplificerede eksempel viser strukturen i den
eksisterende tabel:

id, frugt, farve, form

fx:
1,banan,gul,krum
2,æble,rød,rund
3,avocado,grøn,pæreformet

Den nye struktur er således:

id, feltnavn, værdi

fx:
1,frugt,banan
1,farve,gul
1,form,krum

2,frugt,æble
2,farve,rød
2,form,rund

osv.

Dvs. man i den eksisterende struktur kan sige :
SELECT frugt,farve,form FROM frugter where id=1

I den nye struktur må man sige fx:
SELECT værdi FROM mintabel where id=1 AND feltnavn="frugt"
SELECT værdi FROM mintabel where id=1 AND feltnavn="farve"
SELECT værdi FROM mintabel where id=1 AND feltnavn="form"

Årsagen til den nye struktur er at brugerne selv kan oprette felterne,
og i den eksisterende struktur skal hver ændring afspejles af en
strukturel ændring i databasen hvilket er noget rod. I den nye struktur
skal der ikke ændres noget i databasen uanset hvor mange felter brugeren
vil oprette.

Spørgsmålet er nu om man kan lave fx et (self)JOIN som konverterer den
nye struktur til den gamle. Ideen er at lave et view i den nye struktur
som danner den flade struktur fra den gamle DB.

Jeg har prøvet forskellige variationer over dette tema:

SELECT t2.værdi AS frugt, t3.værdi AS farve, t4.værdi AS form
FROM mintabel t1
INNER JOIN mintabel t2
ON t1.id=t2.id AND t2.feltnavn="frugt"
INNER JOIN mintabel t3
ON t1.id=t3.id AND t3.feltnavn="farve"
INNER JOIN mintabel t4
ON t1.id=t4.id AND t4.feltnavn="form"
WHERE id=1

Men det giver for mange rækker - det skal jo kun give én. Og rækkefølgen
af felterne passer heller ikke.

Er der nogen som har ideer til hvordan dette kan laves?
Hvis jeg ikke har forklaret mig grundigt nok, så sig endelig til!

Det er i MySQL 5.

Hilsen og tak på forhånd
Martin

 
 
Jesper (30-04-2009)
Kommentar
Fra : Jesper


Dato : 30-04-09 17:39

Hej Martin,

Jeg kender kun MSSQL så du skal omskrive det lidt. En måde kan være:

select
id,
(select værdi from mintabel t2 where t2.id=t1.id and feltnavn='frugt') as
frugt,
(select værdi from mintabel t2 where t2.id=t1.id and feltnavn='farve') as
farve,
(select værdi from mintabel t2 where t2.id=t1.id and feltnavn='form') as
form
from mintabel t1
group by id


Jeg ved ikke hvordan du vil have output rækkefølgen, men man bør under alle
omstændigheder bruge ORDER BY og ikke stole på at data kommer ud i den
rækkefølge de er sat ind.

Ovenstående virker kun hvis du retter den hver gang der bliver tilføjet en
ny attribut, idet du skal indsætte en ny linie for at få en ny kolonne med.
(Hvis det er det du ønsker).

Hvis du vil have noget mere dynamisk som automatisk laver en ny
outputkolonne for hvert attribut skal du gøre noget andet. Du kan f.ex. have
en procedure der automatisk genererer noget som den ovenstående SQL hver
gang der bliver tilføjet et attribut. Eksempelvis en trigger på tabellen der
genererer/opdaterer den SQL der beskriver viewet.

Alternativt kig/søg på GROUP_CONCAT og CONCAT_WS som er MySQL
funktionalitet, eller google eventuelt PIVOT og CrossTab som er de ord man
bruger for at beskrive din (ret velkendte) problemstilling.

Mvh,
Jesper



Martin Larsen (01-05-2009)
Kommentar
Fra : Martin Larsen


Dato : 01-05-09 08:07

Hej Jesper

> group by id

Mit første forsøg var faktisk næsten magen til dit forslag, bortset fra
at jeg ikke havde GROUP BY med. Det tænkte jeg ikke på. Og jeg tror at
det gør hele forskellen, så nu vil jeg prøve den taktik igen.

> Alternativt kig/søg på GROUP_CONCAT og CONCAT_WS som er MySQL
> funktionalitet, eller google eventuelt PIVOT og CrossTab som er de ord man
> bruger for at beskrive din (ret velkendte) problemstilling.

Jeg vidste egentligt godt det var en velkendt problemstilling, men kunne
ikke sætte navn på. Det er problemet med googling, normalt er det let at
finde det man søger, men har man ikke et navn på det, kan det være ret
så bøvlet!

Hilsen
Martin

Kaj Julius (30-04-2009)
Kommentar
Fra : Kaj Julius


Dato : 30-04-09 19:15


"Martin Larsen" <want@no.spam> skrev i en meddelelse
news:gtag2l$2ur9$1@newsbin.cybercity.dk...
> Hej alle
>
> I forbindelse med en opgave hvor en applikation skal tilpasses en helt ny
> tabelstruktur med et meget stort konverteringsarbejde til følge, er jeg
> kommet frem til at den bedste og hurtigste løsning er at lave et view i
> den nye DB som "emulerer" den gamle struktur. Hvis dette er muligt, skal
> der kun rettes minimalt i applikationen.
>
> Min første tanke var at det måtte være muligt, men efter en masse
> overvejelser og eksperimenter er jeg knap så sikker!
>
> Nedenstående stærkt simplificerede eksempel viser strukturen i den
> eksisterende tabel:
>
> id, frugt, farve, form
>
> fx:
> 1,banan,gul,krum
> 2,æble,rød,rund
> 3,avocado,grøn,pæreformet
>
> Den nye struktur er således:
>
> id, feltnavn, værdi
>
> fx:
> 1,frugt,banan
> 1,farve,gul
> 1,form,krum
>
> 2,frugt,æble
> 2,farve,rød
> 2,form,rund
>
> osv.
>
> Dvs. man i den eksisterende struktur kan sige :
> SELECT frugt,farve,form FROM frugter where id=1
>
> I den nye struktur må man sige fx:
> SELECT værdi FROM mintabel where id=1 AND feltnavn="frugt"
> SELECT værdi FROM mintabel where id=1 AND feltnavn="farve"
> SELECT værdi FROM mintabel where id=1 AND feltnavn="form"
>
> Årsagen til den nye struktur er at brugerne selv kan oprette felterne, og
> i den eksisterende struktur skal hver ændring afspejles af en strukturel
> ændring i databasen hvilket er noget rod. I den nye struktur skal der ikke
> ændres noget i databasen uanset hvor mange felter brugeren vil oprette.
>
> Spørgsmålet er nu om man kan lave fx et (self)JOIN som konverterer den nye
> struktur til den gamle. Ideen er at lave et view i den nye struktur som
> danner den flade struktur fra den gamle DB.
>
> Jeg har prøvet forskellige variationer over dette tema:
>
> SELECT t2.værdi AS frugt, t3.værdi AS farve, t4.værdi AS form
> FROM mintabel t1
> INNER JOIN mintabel t2
> ON t1.id=t2.id AND t2.feltnavn="frugt"
> INNER JOIN mintabel t3
> ON t1.id=t3.id AND t3.feltnavn="farve"
> INNER JOIN mintabel t4
> ON t1.id=t4.id AND t4.feltnavn="form"
> WHERE id=1
>
> Men det giver for mange rækker - det skal jo kun give én. Og rækkefølgen
> af felterne passer heller ikke.
>
> Er der nogen som har ideer til hvordan dette kan laves?
> Hvis jeg ikke har forklaret mig grundigt nok, så sig endelig til!
>
> Det er i MySQL 5.
>
> Hilsen og tak på forhånd
> Martin

Jeg vil mene, at du er på rette spor med din forespørgsel, men har en join
for meget med:

CREATE VIEW mitview AS (
SELECT t1.id, t1.værdi AS frugt, t2.værdi AS farve, t3.værdi AS form
FROM mintabel t1
INNER JOIN mintabel t2 ON t1.id=t2.id AND t2.feltnavn="farve"
INNER JOIN mintabel t3 ON t1.id=t3.id AND t3.feltnavn="form"
WHERE t1.feltnavn="frugt")

Så burde du kunne bruge din normale forespørgsel mod dette view.

Måske skulle du også overveje at bruge en LEFT OUTER JOIN i viewet i stedet
for INNER JOIN, hvis du ikke er absolut sikker på, at der ALTID bliver
indtastet en række til de to attributter, farve og form.






Kaj Julius (30-04-2009)
Kommentar
Fra : Kaj Julius


Dato : 30-04-09 19:43

Fra dit oprindelige indlæg:

"Årsagen til den nye struktur er at brugerne selv kan oprette felterne,
og i den eksisterende struktur skal hver ændring afspejles af en
strukturel ændring i databasen hvilket er noget rod. I den nye struktur
skal der ikke ændres noget i databasen uanset hvor mange felter brugeren
vil oprette."

Det lyder jo meget tilforladeligt, men et view er pr. definition en fast
struktureret foruddefineret forespørgsel. Dvs. at den ikke er dynamisk,
sådan at hvis der bliver tilføjet et nyt "felt", vil den automatisk blive
tilføjet som et felt i denne forespørgsel. Her vil du alligevel være nødt
til at nedlægge det gamle view og oprette et nyt, hvor der bliver lavet en
self-join til tabellen med det nye "felt" som kriterie. Altså kunne du i
princippet ligeså godt have tilføjet feltet til den oprindelige tabel.

Hvis du vil bibeholde en "emuleret" gammel tabelstruktur i de data dine
programmerne læser, falder dit argument for den nye struktur lidt til
jorden, synes jeg.

Men jeg har jo ikke hørt alle dine argumenter og overvejelser, så se bare
bort fra mit "øregas".



Martin Larsen (01-05-2009)
Kommentar
Fra : Martin Larsen


Dato : 01-05-09 08:16

Kaj Julius wrote:

> Men jeg har jo ikke hørt alle dine argumenter og overvejelser, så se bare
> bort fra mit "øregas".

Du har helt ret i alle dine argumenter, men det er slags bridge imellem
de to databaser jeg skal lave, og i det konkrete tilfælde er strukturen
"frosset" så der ikke sker ændringer i strukturen foreløbig.

Når man skriver et indlæg er det altid en balancegang mellem at få det
nødvendige, og kun det nødvendige med, men alligevel kan man godt komme
til at oversimplifice sagen

Hilsen
Martin

Martin Larsen (01-05-2009)
Kommentar
Fra : Martin Larsen


Dato : 01-05-09 08:11

Hej Kaj

> Jeg vil mene, at du er på rette spor med din forespørgsel, men har en join
> for meget med:

Det er nok rigtigt at der var et join for meget, men jeg tror stadig at
dit forslag vil give flere rækker pr. "frugt". Godt nok vil rækkerne
(eller i al fald en af dem) indeholde de rigtige felter, men de to
øvrige vil nok lave ballade idet applikationen kun forventer ét resultat
for en giver ID. Hvis jeg vidste at række 1 indeholder de rigtige felter
kunne jeg selvfølgelig prøve med en LIMIT 1.

Hilsen
Martin

Kaj Julius (01-05-2009)
Kommentar
Fra : Kaj Julius


Dato : 01-05-09 13:10


"Martin Larsen" <want@no.spam> skrev i en meddelelse
news:gte79p$16t4$1@newsbin.cybercity.dk...
> Hej Kaj
>
>> Jeg vil mene, at du er på rette spor med din forespørgsel, men har en
>> join for meget med:
>
> Det er nok rigtigt at der var et join for meget, men jeg tror stadig at
> dit forslag vil give flere rækker pr. "frugt". Godt nok vil rækkerne
> (eller i al fald en af dem) indeholde de rigtige felter, men de to øvrige
> vil nok lave ballade idet applikationen kun forventer ét resultat for en
> giver ID. Hvis jeg vidste at række 1 indeholder de rigtige felter kunne
> jeg selvfølgelig prøve med en LIMIT 1.
>
> Hilsen
> Martin

"..men jeg tror stadig at dit forslag vil give flere rækker pr. "frugt" -
det mener jeg netop, at det ikke gør. Den ekstra self-join bevirkede jo
netop, at der kom tre rækker for hvert id.

Har du prøvet at oprette det viste view og så lavet en forespørgsel mod
viewet, hvor du udvælger et enkelt id:

SELECT * FROM mitView WHERE id = 1

Efter min mening burde det kun give 1 række.



Martin Larsen (09-05-2009)
Kommentar
Fra : Martin Larsen


Dato : 09-05-09 11:12

Hej Kaj

> Efter min mening burde det kun give 1 række.

Jeg har nu haft tid til at prøve det, og du har jo ganske ret: de virker
helt fint!

Jeg hæftede mig kun ved at du havde fjernet en join, hvilket ikke i sig
selv ville ændre på antallet af rækker. Ændringen i WHERE delen
(t1.feltnavn="frugt) havde jeg ikke noteret mig.

Jespers forslag virker også. Her var det GROUP BY som jeg manglede.

Tak til jer begge to!

Hilsen
Martin

Søg
Reklame
Statistik
Spørgsmål : 177414
Tips : 31962
Nyheder : 719565
Indlæg : 6407849
Brugere : 218876

Månedens bedste
Årets bedste
Sidste års bedste