Vinnaren i pepparkakshustävlingen!
2013-08-23, 12:23
  #1
Medlem
Har följande struktur på databasen: tag_to_ingredient är kopplingstabellen mellan ingredient och tag.
Varje ingrediens kan ha flera taggar och varje tagg kan höra till flera ingredienser.

Hur kan jag få ut alla ingredienser som har både tag_id 1 och 2 (och kanske även andra). (Exempel på tag_to_ingredients innehåll.) Vill även att antalet taggar ska kunna vara dynamiskt vid sökning. Ansluter via PHP och PDO.

TABLE tag_to_ingredient(
ingredient_id INT(10) UNSIGNED NOT NULL,
tag_id INT(10) UNSIGNED NOT NULL,
FOREIGN KEY (ingredient_id) REFERENCES ingredient (ingredient_id),
FOREIGN KEY (tag_id) REFERENCES tag (tag_id),
PRIMARY KEY (ingredient_id, tag_id)

TABLE ingredient(
ingredient_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(40) NOT NULL,
FOREIGN KEY (ingredient_brand_id) REFERENCES ingredient_brand (ingredient_brand_id),
PRIMARY KEY (ingredient_id)

TABLE tag(
tag_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(40) NOT NULL,
PRIMARY KEY (tag_id)
__________________
Senast redigerad av raecer 2013-08-23 kl. 12:28.
Citera
2013-08-23, 12:45
  #2
Medlem
FSharps avatar


SELECT * FROM INGREDIENTS as ING

INNER JOIN TAGINGREDIENTS AS TI ON (ING.ID = TI.INGID AND (TI.TAGID = 1 OR TI.TAGID = 2))

?

Jag använde lite enklare namn, du bör förstå.
Citera
2013-08-24, 23:21
  #3
Medlem
fnirps avatar
Citat:
Ursprungligen postat av raecer
Hur kan jag få ut alla ingredienser som har både tag_id 1 och 2 (och kanske även andra). Vill även att antalet taggar ska kunna vara dynamiskt vid sökning. Ansluter via PHP och PDO.

Eftersom du har id-numrena som sökvillkor, behövs egentligen inte tabellen "tag", då denna information finns som främmande nyckel i "tag_to_ingredient". Dock tror jag att en sökning på tag-namn är mer realistisk och användbar så här kommer min lösning:

Kod:
SELECT i.name
FROM ingredient i
JOIN tag_to_ingredient tti on i.ingredient_id = tti.ingredient_id
JOIN tag t on tti.tag_id = t.tag_id
WHERE t.name in ('tag1namn', 'tag2namn')
GROUP BY i.name

Alternativt om det är så att du verkligen vill söka specifikt på tag-id:s

Kod:
SELECT i.name
FROM ingredient i
JOIN tag_to_ingredient tti on i.ingredient_id = tti.ingredient_id
WHERE tti.tag_id in (1,2)
GROUP BY i.name

Till skillnad från FSharp, väljer jag att lägga filtreringen i where-satsen, eftersom den semantiskt inte har med ihopkopplingen av tabeller att göra. Resultatmässigt är det vilket som :-)

Har dock lagt till en gruppering som gör att varje ingrediens bara kommer upp en gång, där skiljer sig min och FSharps lösningar åt.
Citera
2013-08-25, 16:40
  #4
Medlem
John-Pauls avatar
Citat:
Ursprungligen postat av fnirp
Eftersom du har id-numrena som sökvillkor, behövs egentligen inte tabellen "tag", då denna information finns som främmande nyckel i "tag_to_ingredient". Dock tror jag att en sökning på tag-namn är mer realistisk och användbar så här kommer min lösning:

Kod:
SELECT i.name
FROM ingredient i
JOIN tag_to_ingredient tti on i.ingredient_id = tti.ingredient_id
JOIN tag t on tti.tag_id = t.tag_id
WHERE t.name in ('tag1namn', 'tag2namn')
GROUP BY i.name

Alternativt om det är så att du verkligen vill söka specifikt på tag-id:s

Kod:
SELECT i.name
FROM ingredient i
JOIN tag_to_ingredient tti on i.ingredient_id = tti.ingredient_id
WHERE tti.tag_id in (1,2)
GROUP BY i.name

Till skillnad från FSharp, väljer jag att lägga filtreringen i where-satsen, eftersom den semantiskt inte har med ihopkopplingen av tabeller att göra. Resultatmässigt är det vilket som :-)

Har dock lagt till en gruppering som gör att varje ingrediens bara kommer upp en gång, där skiljer sig min och FSharps lösningar åt.

Är inte DISTINCT mer naturligt om det är unika name man vill ha, ger samma resultat med en ev. mindre belastning.
Citera
2013-08-25, 19:30
  #5
Medlem
fnirps avatar
Citat:
Ursprungligen postat av John-Paul
Är inte DISTINCT mer naturligt om det är unika name man vill ha, ger samma resultat med en ev. mindre belastning.

GROUP BY är ju mer generellt än SQL Server specifika DISTINCT, men kör man det senare är det nog en smaksak vilken approach man väljer. GROUP BY blir oftast mer att skriva då man måste spec:a alla resultatkolumner två gånger (en gång i SELECT och en gång till i GROUP BY, något som blir extra besvärligt om man döper om alla kolumner i SELECT-delen), Har dock hört att GROUP BY ska vara snällare mot databasmotorn i SQL Server, men huruvida det är sant låter jag vara osagt :-)
Citera
2013-08-25, 23:06
  #6
Medlem
John-Pauls avatar
Jag trodde att distinct var standard sql (orkar inte kolla nu) och jag var lite otydlig för jag vet inte vilken av metoderna som är "snällast" (därav "ev.")
__________________
Senast redigerad av John-Paul 2013-08-25 kl. 23:08.
Citera
2013-08-26, 08:24
  #7
Medlem
fnirps avatar
Citat:
Ursprungligen postat av John-Paul
Jag trodde att distinct var standard sql (orkar inte kolla nu) och jag var lite otydlig för jag vet inte vilken av metoderna som är "snällast" (därav "ev.")

Jag som var trött och felkopplad i huvudet. Självklart är DISTINCT standard-SQL och inget annat. Ber ödmjukast om ursäkt för förvirringen jag må ha orsakat...
Citera
2013-08-27, 14:08
  #8
Medlem
Citat:
Ursprungligen postat av fnirp
Eftersom du har id-numrena som sökvillkor, behövs egentligen inte tabellen "tag", då denna information finns som främmande nyckel i "tag_to_ingredient". Dock tror jag att en sökning på tag-namn är mer realistisk och användbar så här kommer min lösning:

Kod:
SELECT i.name
FROM ingredient i
JOIN tag_to_ingredient tti on i.ingredient_id = tti.ingredient_id
JOIN tag t on tti.tag_id = t.tag_id
WHERE t.name in ('tag1namn', 'tag2namn')
GROUP BY i.name


Har dock lagt till en gruppering som gör att varje ingrediens bara kommer upp en gång, där skiljer sig min och FSharps lösningar åt.

Uppskattar hjälpen

När jag använder exemplet ovan så får jag ingredienser som är taggade med bägge taggarna men även ingredienser taggade med antingen eller. Editerade det enligt nedan ifrån en annan sida men jag förstår ej exakt vad den gör. Filtrerar den helt enkelt bara ut dem som har exakt båda träffarna? Eller räknar den bara sista träffen?

Testade att lägga till DISTINCT istället för GROUP BY istället (samma resultat som group by, fast då får jag inga svar med COUNT)

Citat:
SELECT i.name
FROM ingredient i
JOIN tag_to_ingredient tti on i.ingredient_id = tti.ingredient_id
JOIN tag t on tti.tag_id = t.tag_id
WHERE t.name in ('tag1namn', 'tag2namn')
GROUP BY i.name
HAVING COUNT(*) = 2

EDIT: verkar som att count returnerar bara träffar med exakt dem två taggarna. Får det dock inte att fungera med DISTINCT då HAVING bara verkar fungera med GROUP BY.

Är det en ok lösning detta eller bör jag leta andra alternativ som kanske är bättre prestandamässigt?
__________________
Senast redigerad av raecer 2013-08-27 kl. 14:14.
Citera
2013-08-27, 21:01
  #9
Medlem
John-Pauls avatar
Jag har inte satt mig in din modell och den verkar heller inte fullt ut beskriven eftersom jag inte kan se ingredient_brand men jag ska ändå försöka mig på att svar lite.

Kod:
SELECT i.name
FROM ingredient i
JOIN tag_to_ingredient tti on i.ingredient_id = tti.ingredient_id
JOIN tag t on tti.tag_id = t.tag_id
WHERE t.name in ('tag1namn', 'tag2namn')
GROUP BY i.name

Och din fråga:

När jag använder exemplet ovan så får jag ingredienser som är taggade med bägge taggarna men även ingredienser taggade med antingen eller. Editerade det enligt nedan ifrån en annan sida men jag förstår ej exakt vad den gör. Filtrerar den helt enkelt bara ut dem som har exakt båda träffarna? Eller räknar den bara sista träffen?

Så kanske detta är begripligt:
IN ger dig alla rader där t.name är lika med 'tag1namn' eller 'tag2namn'
Det är det samma som: Undanta alla rader där t.name inte är lika med 'tag1namn' eller 'tag2namn'
Finns det flera rader med ett och samma t.name som är något av 'tag1namn' eller 'tag2namn' så kommer dessa med i ditt resultat.

Vill du bara ha unika t.namn så kan man använda DISTINCT eller GROUP BY, HAVING COUNT(*) är frivilligt villkor som används tillsammans med GROUP BY för att begränsa vilka grupper man vill ha och då gäller det antalet per grupp.
Enkelt exempel:

Du hur personer med namn tabellen person där samma namn kan förekomma flera gånger, nu vill du veta vilka namn som är unika, alltså som bara förekommer n gång
SELECT namn FROM person group by namn HAVING COUNT(*) = 1
Du vill veta vilka namn som förekommer ett visst antal gånger, säg 3
SELECT namn FROM person group by namn GROUP BY namn HAVING COUNT(*) = 3

Om du vill veta alla namn och bara se varje man en gång så:
SELECT DISTINCT namn FROM person
eller
SELECT namn FROM person GROUP BY namn

Vill du veta hur många gånger varje namn finns så:
SELECT namn,count(*) FROM person GROUP BY namn
__________________
Senast redigerad av John-Paul 2013-08-27 kl. 21:22.
Citera
2013-08-28, 09:28
  #10
Medlem
Citat:
Ursprungligen postat av John-Paul
Jag har inte satt mig in din modell och den verkar heller inte fullt ut beskriven eftersom jag inte kan se ingredient_brand men jag ska ändå försöka mig på att svar lite.

Kod:
SELECT i.name
FROM ingredient i
JOIN tag_to_ingredient tti on i.ingredient_id = tti.ingredient_id
JOIN tag t on tti.tag_id = t.tag_id
WHERE t.name in ('tag1namn', 'tag2namn')
GROUP BY i.name

Och din fråga:

När jag använder exemplet ovan så får jag ingredienser som är taggade med bägge taggarna men även ingredienser taggade med antingen eller. Editerade det enligt nedan ifrån en annan sida men jag förstår ej exakt vad den gör. Filtrerar den helt enkelt bara ut dem som har exakt båda träffarna? Eller räknar den bara sista träffen?

Så kanske detta är begripligt:
IN ger dig alla rader där t.name är lika med 'tag1namn' eller 'tag2namn'
Det är det samma som: Undanta alla rader där t.name inte är lika med 'tag1namn' eller 'tag2namn'
Finns det flera rader med ett och samma t.name som är något av 'tag1namn' eller 'tag2namn' så kommer dessa med i ditt resultat.

Vill du bara ha unika t.namn så kan man använda DISTINCT eller GROUP BY, HAVING COUNT(*) är frivilligt villkor som används tillsammans med GROUP BY för att begränsa vilka grupper man vill ha och då gäller det antalet per grupp.
Enkelt exempel:

Du hur personer med namn tabellen person där samma namn kan förekomma flera gånger, nu vill du veta vilka namn som är unika, alltså som bara förekommer n gång
SELECT namn FROM person group by namn HAVING COUNT(*) = 1
Du vill veta vilka namn som förekommer ett visst antal gånger, säg 3
SELECT namn FROM person group by namn GROUP BY namn HAVING COUNT(*) = 3

Om du vill veta alla namn och bara se varje man en gång så:
SELECT DISTINCT namn FROM person
eller
SELECT namn FROM person GROUP BY namn

Vill du veta hur många gånger varje namn finns så:
SELECT namn,count(*) FROM person GROUP BY namn

Tack så mycket för förklaringen!
Fungerar superbra, även med flera tags
Ska se om jag kan konstruera en query via php med valfritt antal taggar nu. Tack igen
Citera

Stöd Flashback

Flashback finansieras genom donationer från våra medlemmar och besökare. Det är med hjälp av dig vi kan fortsätta erbjuda en fri samhällsdebatt. Tack för ditt stöd!

Stöd Flashback