order by Problem im join
Sebastian Fischer
- datenbank
Ich habe in folgendem Join das Problem, dass mir nicht der aktuelle Preis ermittelt wird sondern ein beliebig alter Preis.
Was ich mir von dem Join erhoffe ist, dass mir der Besitzer der Produktliste ausgegeben wird (Prüfung ob berechtigt zur Anzeige), das die Produktdaten ausgegeben werden (für die Detailansicht) und das mir der niedrigste Preis und der aktuelle Preis ermittelt werden.
Verwendung finden 5 Tabellen:
tx_sfpricetracker_lists - verschiedene Produktlisten die von Usern gepflegt werden können
-- #uid, name, description, owner
tx_sfpricetracker_lists_products_mm - m:n Verknüpfung von Listen und Produkte
-- #uid_local, #uid_foreign
tx_sfpricetracker_products - Produkte die gepflegt werden können
-- #uid, name, description, producer, amount, link
tx_sfpricetracker_products_prices_mm - m:n Verknüpfung von Produkte zu Preise
-- #uid_local, #uid_foreign
tx_sfpricetracker_prices - einzel Preise
-- #uid, crdate, price
Für die Ausgabe der Detailansicht habe ich folgenden State erstellt, der mir aber wie bereits gesagt leider nicht den aktuellen Preis ausgibt.
SELECT
lists.owner,
products.*,
MIN(prices.price) AS lowestprice,
prices.price AS currentprice,
prices.crdate
FROM
tx_sfpricetracker_lists AS lists,
tx_sfpricetracker_lists_products_mm AS lp,
tx_sfpricetracker_products AS products,
tx_sfpricetracker_products_prices_mm AS pp,
tx_sfpricetracker_prices AS prices
WHERE
products.uid = 4
AND lists.owner = 1
AND lists.uid = lp.uid_local
AND products.uid = lp.uid_foreign
AND products.uid = pp.uid_local
AND prices.uid = pp.uid_foreign
GROUP BY products.uid
ORDER BY prices.crdate desc
Was mach ich da falsch, warum wird mir zwar der minimale Preis korrekt ausgegeben, aber nicht der aktuelle? Und was kann ich ändern damit es dennoch funktioniert?
Hallo Sebastian,
Ich habe in folgendem Join das Problem, dass mir nicht der aktuelle Preis ermittelt wird sondern ein beliebig alter Preis.
ja, genauso wie es Dir die Dokumentation Deines Datenbankmanagementsystems sagt. Meine Glaskugel sagt mir, dass Du MySQL benutzt.
SELECT
lists.owner,
products.*,
MIN(prices.price) AS lowestprice,
prices.price AS currentprice,
prices.crdateFROM
tx_sfpricetracker_lists AS lists,
tx_sfpricetracker_lists_products_mm AS lp,
tx_sfpricetracker_products AS products,
tx_sfpricetracker_products_prices_mm AS pp,
tx_sfpricetracker_prices AS pricesWHERE
products.uid = 4
AND lists.owner = 1
AND lists.uid = lp.uid_local
AND products.uid = lp.uid_foreign
AND products.uid = pp.uid_local
AND prices.uid = pp.uid_foreign
Warum nicht saubere JOIN-Syntax? Du findest unter SELFHTML Aktuell im Bereich Datenbanken zwei Artikel zu diesem Thema.
GROUP BY products.uid
ORDER BY prices.crdate descWas mach ich da falsch, warum wird mir zwar der minimale Preis korrekt ausgegeben,
weil Du den ermittelst.
aber nicht der aktuelle?
weil Du den nirgendwo ermittelst.
Und was kann ich ändern damit es dennoch funktioniert?
Zuerst Deine MySQL-Version angeben. Davon ist abhängig, was Du machen kannst. Wirklich? Ja, wirklich! Hoffentlich hast Du MySQL 4.1 oder neuer.
Recht ausführlich habe ich das in mehreren Beiträgen zu einer ähnlichen Problematik in folgendem Archivthread behandelt.
Freundliche Grüße
Vinzenz
Hallo Vinzenz,
danke für Deine Antwort. Jetzt zu den noch offenen Punkten.
Mein verfügbarer SQL Server ist MySQL 4.0.24-standard.
Warum nicht saubere JOIN-Syntax? Du findest unter SELFHTML Aktuell im Bereich Datenbanken zwei Artikel zu diesem Thema.
Der SQL State findet in einem CMS Verwendung. Ob da die komplette Join Syntax umgesetzt werden kann, habe ich leider noch nicht komplett ausgetestet, deshalb der zugegebenermassen etwas umständliche Aufbau.
aber nicht der aktuelle?
weil Du den nirgendwo ermittelst.
Ich war davon ausgegangen, dass der aktuelle Preis automatisch der erste Preis ist der durch die Sortierung ausgegeben wird. Also Preise nach Datum sortiert und dann den ersten der dabei raus kommt. deshalb dachte ich, dass prices.price AS currentprice ausreicht.
Recht ausführlich habe ich das in mehreren Beiträgen zu einer ähnlichen Problematik in folgendem Archivthread behandelt.
Werde mich dann doch noch mehr mit dem Join auseinandersetzen müssen. Hättest Du eventuell trotzdem einen Hotfix für das Problem, oder muss dafür der Komplette State umgebaut werden?
Dank und viele Grüße
Sebastian
Moin!
aber nicht der aktuelle?
weil Du den nirgendwo ermittelst.
Ich war davon ausgegangen, dass der aktuelle Preis automatisch der erste Preis ist der durch die Sortierung ausgegeben wird. Also Preise nach Datum sortiert und dann den ersten der dabei raus kommt. deshalb dachte ich, dass prices.price AS currentprice ausreicht.
MySQL erlaubt dir unsinnigerweise, dass du im SELECT auch Spalten angeben darfst, die weder die Aggregatfunktionen (MIN, MAX, COUNT, SUM,...) nutzen, noch bei GROUP BY aufgelistet sind.
Werde mich dann doch noch mehr mit dem Join auseinandersetzen müssen.
Nein, mit dem GROUP BY.
Dein JOIN erzeugt dir eine lange und breite Liste mit vielen Zeilen. Das könnte genausogut auch in einer einzigen Tabelle drinstehen - für die Problematik des GROUP BY ist das egal.
- Sven Rautenberg
yo,
MySQL erlaubt dir unsinnigerweise, dass du im SELECT auch Spalten angeben darfst, die weder die Aggregatfunktionen (MIN, MAX, COUNT, SUM,...) nutzen, noch bei GROUP BY aufgelistet sind.
mysql erlaubt es nicht, wenn sich die werte der entsprechenden spalte unterscheiden, was hier der fall sein sollte. nur gibt das dbms keine fehlmerldung aus. das wirft die frage aus, gibt es einen syntaktischen fehler ohne fehlermeldung ? bei mysql ist es ganz offensichtlich so.
Ilja
Moin!
MySQL erlaubt dir unsinnigerweise, dass du im SELECT auch Spalten angeben darfst, die weder die Aggregatfunktionen (MIN, MAX, COUNT, SUM,...) nutzen, noch bei GROUP BY aufgelistet sind.
mysql erlaubt es nicht, wenn sich die werte der entsprechenden spalte unterscheiden, was hier der fall sein sollte.
Doch! Ist in der Doku auch beschrieben, in welchen Fällen man dieses Feature (aus Geschwindigkeitsgründen) nutzen soll: Wenn sich die zusammengruppierten Spalten nicht unterscheiden, andernfalls wird ein zufälliges Ergebnis produziert.
nur gibt das dbms keine fehlmerldung aus. das wirft die frage aus, gibt es einen syntaktischen fehler ohne fehlermeldung ? bei mysql ist es ganz offensichtlich so.
Ein Fehler ist, wenn etwas nicht wie beschrieben funktioniert. Bei MySQL funktioniert das selektieren nicht gruppierter Spalten wie beschrieben, also ist das kein Fehler. Die Erwartungshaltung des Nutzers paßt nur meist nicht zum produzierten Ergebnis - also ist die Erwartungshaltung fehlerhaft.
Aber das kann man ja mit einem Update der Erwartungshaltung korrigieren.
- Sven Rautenberg
Nein, mit dem GROUP BY.
- Sven Rautenberg
Hallo Sven,
zum einen habe ich den Join überarbeitet, damit wenigstens da kein(weniger?) Fehler mehr auftreten, zum anderen habe ich keine Probleme mit dem Ergebniss von MIN(). Das heißt die Ergebnisse des GROUP BY sind wie vorher gesehen richtig. Das Problem entsteht bei dem Wert der nicht gegroupt wird.
Zu den Spalten kann ich nur sagen, dass bis auf den Preis und das Datum des Preises alle Werte einer ProduktID gleich sind. Das heisst bei Übergabe einer ProduktID variieren einzig und alleine die Preise und deren jeweiliges Erstellungsdatum.
Problem ist, dass ich nicht das letzte Datum erhalte sondern ein beliebiges. Es ist also immer noch die Frage offen, warum ich trotz ORDER BY nicht an das richtige Ergebniss gelange.
Ich bin jetzt mal testweise hergegangen und habe alle abgefragten Spalten im GROUP BY eingefügt. So lange ich alle Spalten einfüge und die currentprice Spalte weglasse, erhalte ich als Ergbeniss korrekte Werte für die Aggregatsfunktionen und für eindeutigen Werte des Preises. Nur wird dann immer noch nicht der richtige currentprice ausgegeben.
Schliesse ich den currentprice zusätzlich noch mit ein erhalte ich mehrere Zeilen und die Aggregatsfunktionen liefern mir Mist zurück.
Zur Verdeutlichung hier mal das State und die beiden Ergebnisse:
SELECT
tx_sfpricetracker_lists.owner,
tx_sfpricetracker_products.producer,
tx_sfpricetracker_products.name,
tx_sfpricetracker_products.description,
tx_sfpricetracker_products.link,
tx_sfpricetracker_products.amount,
MIN(tx_sfpricetracker_prices.price) AS lowestprice,
MAX(tx_sfpricetracker_prices.price) AS highestprice,
tx_sfpricetracker_prices.price AS currentprice
FROM
tx_sfpricetracker_products
INNER JOIN (tx_sfpricetracker_products_prices_mm
INNER JOIN tx_sfpricetracker_prices ON tx_sfpricetracker_products_prices_mm.uid_foreign = tx_sfpricetracker_prices.uid
) ON tx_sfpricetracker_products.uid = tx_sfpricetracker_products_prices_mm.uid_local
INNER JOIN (tx_sfpricetracker_lists_products_mm
INNER JOIN tx_sfpricetracker_lists ON tx_sfpricetracker_lists_products_mm.uid_local = tx_sfpricetracker_lists.uid
) ON tx_sfpricetracker_products.uid = tx_sfpricetracker_lists_products_mm.uid_foreign
WHERE
tx_sfpricetracker_products.uid = 2
AND tx_sfpricetracker_lists.owner = 1
GROUP BY
tx_sfpricetracker_lists.owner,
tx_sfpricetracker_products.producer,
tx_sfpricetracker_products.name,
tx_sfpricetracker_products.description,
tx_sfpricetracker_products.link,
tx_sfpricetracker_products.amount,
currentprice <------- durch weglassen ändert sich das Ergebniss
ORDER BY tx_sfpricetracker_prices.crdate desc
Ergebniss mit currentprice in GROUP BY:
"owner","producer","name","description","link","amount","lowestprice","highestprice","currentprice"
"1","Samsung","250GB Samsung SP2504C SATAII","Festplatte Sata","http://www.mindfactory.de/cgi-bin/MindStore.storefront/DE/Product/0017325",1,"62.70","62.70","62.70"
"1","Samsung","250GB Samsung SP2504C SATAII","Festplatte Sata","http://www.mindfactory.de/cgi-bin/MindStore.storefront/DE/Product/0017325",1,"62.69","62.69","62.69"
"1","Samsung","250GB Samsung SP2504C SATAII","Festplatte Sata","http://www.mindfactory.de/cgi-bin/MindStore.storefront/DE/Product/0017325",1,"58.54","58.54","58.54"
Wunderbar sortierter Preis mit aktuellem Preis oben aber total vergurkte Aggregatfunktionen.
Ohne currentprice in GROUP BY:
"owner","producer","name","description","link","amount","lowestprice","highestprice","currentprice"
"1","Samsung","250GB Samsung SP2504C SATAII","Festplatte Sata","http://www.mindfactory.de/cgi-bin/MindStore.storefront/DE/Product/0017325",1,"58.54","62.70","62.69"
Jetzt sind die Werte der Aggregatsfunktion korrekt aber der currentprice ist dafür nicht der aktuelle sondern ein unvorhersehbarer.
Beim rumexperimentieren mit dem State ist mir dann auch noch aufgefallen, dass wenn ich das GROUP BY und die Aggregatsfunktionen entferne, es zu ein Fehler kommt. Denn obwohl alle Felder eindeutig über den Tabellennamen und den Feldnamen angesprochen werden, erhalte ich die Meldung "Column 'uid' in field list is ambiguous". Das kann aber nicht sein, denn kein Feld wird hier zweideutig verwendet. Zum testen habe ich folgenden auf ein minimum abgespreckten State verwendet:
SELECT
tx_sfpricetracker_lists.owner,
tx_sfpricetracker_prices.price
FROM
tx_sfpricetracker_products
INNER JOIN (tx_sfpricetracker_products_prices_mm
INNER JOIN tx_sfpricetracker_prices ON tx_sfpricetracker_products_prices_mm.uid_foreign = tx_sfpricetracker_prices.uid
) ON tx_sfpricetracker_products.uid = tx_sfpricetracker_products_prices_mm.uid_local
INNER JOIN (tx_sfpricetracker_lists_products_mm
INNER JOIN tx_sfpricetracker_lists ON tx_sfpricetracker_lists_products_mm.uid_local = tx_sfpricetracker_lists.uid
) ON tx_sfpricetracker_products.uid = tx_sfpricetracker_lists_products_mm.uid_foreign
WHERE
tx_sfpricetracker_products.uid = 2
Wenn es nicht am JOIN liegt, dann weiss ich nicht was da falsch sein soll. Davon ab soll der letzte State nicht das Endergebniss sein. Den habe ich nur verwendet um ein Abfrage zu haben die auf ein Minimum an Fehlerquellen reduziert ist. Leider habe ich mich da wohl getäuscht.
So langsam bin ich mit meinem SQL-Latein am Ende.
Gruß
Sebastian
Hallo Sebastian,
zum einen habe ich den Join überarbeitet, damit wenigstens da kein(weniger?) Fehler mehr auftreten, zum anderen habe ich keine Probleme mit dem Ergebniss von MIN(). Das heißt die Ergebnisse des GROUP BY sind wie vorher gesehen richtig. Das Problem entsteht bei dem Wert der nicht gegroupt wird.
Nein. Das Problem ist in MySQL enthalten. Jedes andere mir bekannte Datenbankmanagementsystem weist Deine Abfrage als syntaktisch falsch zurück, was ich persönlich gut finde. MySQL ist großzügig, ist schlampig. Das ändert nichts daran, dass das, was MySQL zurückliefert dem entspricht, was das Handbuch verspricht, ich zitiere aus dem entsprechenden Abschnitt:
<zitat>
Do not use this feature if the columns you omit from the GROUP BY part are not constant in the group. The server is free to return any value from the group, so the results are indeterminate unless all values are the same.
</zitat>
Das Resultat dieser nicht gruppierten Spalte ist unbestimmt. Der Server darf jeden beliebigen Wert aus der Gruppe zurückliefern. Du sollst dieses Feature _nicht_ nutzen, wenn die Werte in dieser Spalte je Gruppe _nicht_ gleich sind.
Problem ist, dass ich nicht das letzte Datum erhalte sondern ein beliebiges. Es ist also immer noch die Frage offen, warum ich trotz ORDER BY nicht an das richtige Ergebniss gelange.
Weil es keinen Grund dafür gibt. Ich hoffe, Du kannst meine Argumentation nachvollziehen. ORDER BY sortiert die Datensätze des Ergebnisses, nicht die der Ausgangsmengen. Und selbst wenn es die Ausgangsdaten sortierte, garantiert Dir MySQL _immer noch nicht_, dass es den "ersten" Wert einer nicht aggregierten Spalte nimmt. MySQL nimmt einen zufälligen.
So langsam bin ich mit meinem SQL-Latein am Ende.
Dein Problem ist mit SQL wunderbar einfach zu lösen. Es ist eine Standardaufgabe, für die es eine Standardlösung gibt: korrelierte Subselects, siehe auch im MySQL-Handbuch.
Der einzige klitzekleine aber leider entscheidende Haken an der Sache ist der, dass MySQL Subselects erst ab Version 4.1 unterstützt, Du aber Version 4.0.24 im Einsatz hast. Im MySQL-Handbuch der älteren Versionen findest Du auch einen sehr schmutzigen Workaround, wie man das mit MySQL 4.0.x und älter in einem einzigen Statement hinbekommt: MAX-CONCAT trick.
Freundliche Grüße
Vinzenz
Hallo Vinzenz,
danke für den Verweis auf den Concat Trick. Das muss ich mir mal in aller Ruhe anschauen, denn mir wird schon nach 5 Zeilen schwindlig wenn ich das versuche zu verstehen.
Habs jetzt erstmal genauso dirty über eine zweite Abfrage erschosse. Da diese Abfrage aber auch in einer Liste Verwendung finde, werde ich genau drauf achten müssen wie sich die Performance verhält bei 2 getrennten Abfragen. Kann sein, dass bei steigender Artikelzahl das ganze bei der aktuellen Version doch zu langsam wird und ich auf den Concat Trick zurück greifen muss.
Danke und einen schönen Tag noch
Sebastian
Hallo Sebastian,
danke für den Verweis auf den Concat Trick. Das muss ich mir mal in aller Ruhe anschauen, denn mir wird schon nach 5 Zeilen schwindlig wenn ich das versuche zu verstehen.
Du könntest Deinen Provider überreden, auf eine neuere MySQL-Version umzusteigen. Das ist allerdings nicht ganz ohne, da ab Version 4.1 die Character-Set-Unterstützung enorm verbessert wurde. Dazu gibt es ein interessantes Handbuchkapitel. Leider haben damit viele Benutzer Probleme, was zu einer Vielzahl von Threads hier im Forum führt (derer sich sehr oft dedlfix annimmt).
In Version 5 wurde dann die JOIN-Unterstützung enorm verbessert und an den SQL-Standard angeglichen. Das Ergebnis ist, dass es eine Reihe von Anwendungen gibt, die deswegen nicht mehr funktionieren. Wenn man die Wahl hat, sollte man nicht unter MySQL 5.0.12 einsteigen. Den Grund für meine Meinung findet man im Handbuchkapitel JOIN.
Ansonsten denke daran, dass der CONCAT-Trick ineffizient ist und falls Du Performanceprobleme hast, Du auf den Workaround mit der temporären Tabelle zurückgreifen könntest. Richtig schön ist keine der Lösungen, die unter MySQL 4.0.x oder älter funktionieren.
Freundliche Grüße
Vinzenz
Moin!
Nein, mit dem GROUP BY.
Hallo Sven,
zum einen habe ich den Join überarbeitet, damit wenigstens da kein(weniger?) Fehler mehr auftreten, zum anderen habe ich keine Probleme mit dem Ergebniss von MIN(). Das heißt die Ergebnisse des GROUP BY sind wie vorher gesehen richtig. Das Problem entsteht bei dem Wert der nicht gegroupt wird.
Es gibt keinen Wert, der nicht gruppiert wird. Alle Werte werden gruppiert, wie sollte das sonst anders gehen.
Zu den Spalten kann ich nur sagen, dass bis auf den Preis und das Datum des Preises alle Werte einer ProduktID gleich sind.
Dann verleihe dieser Tatsache Nachdruck, indem du sämtliche dieser Spalten in dein GROUP BY schreibst.
Das heisst bei Übergabe einer ProduktID variieren einzig und alleine die Preise und deren jeweiliges Erstellungsdatum.
Das bedeutet, dass du für Preis und Datum eine Aggregatfunktion finden mußt, die dir "den aktuellsten Preis" und "das aktuellste Datum" liefert.
Problem: Aggregatfunktionen arbeiten nur auf ihrer eigenen Spalte. Und es gibt keine Funktion "das aktuellste".
Beim Datum helfen zum Glück numerische Vergleiche, du kriegst also mit MAX(datum) das "größte" Datum, was gleichzeitig das aktuellste Datum ist.
Aber du kriegst parallel nicht mit MAX(preis) den aktuellsten Preis, sondern den größten. Und es gibt keine Aggregatfunktion, die dir den Preis liefert aus der Zeile, die MAX(datum) hat.
Für dein Problem ist "Gruppierung" einfach der falsche Ansatz. Du kannst das, was du von der Datenbank wissen willst, nicht mit einer schlichten Gruppierung auslesen! Punkt, aus, Ende!
Und jetzt kommt dein Problem: Du benutzt MySQL 4.0. Damit kannst du auch keine Unterabfragen machen.
Denn im Prinzip mußt du deine Abfrage wie folgt aufbauen:
Unterabfrage: Gruppiere zu jeder Artikel-ID das MAX(datum).
Hauptabfrage: Gib mir den langen JOIN, bei dem die Artikel-ID gleich der Artikel-ID aus der Unterabfrage ist, und das Datum gleich dem MAX(datum) aus der Unterabfrage.
Problem ist, dass ich nicht das letzte Datum erhalte sondern ein beliebiges.
Wie gesagt, das versuchen wir dir hier im Thread schon seit Anbeginn zu vermitteln:
Im SQL-Standard müssen alle Spalten, die als Ergebnis einer GROUP BY-Abfrage auftauchen sollen, entweder durch eine Aggregatfunktion gebildet werden, oder hinter GROUP BY aufgeführt werden.
MySQL erlaubt, auch andere Spalten in das Ergebnis mit aufzunehmen, aber wenn der Spalteninhalt nicht genau wie die Hauptgruppierungsspalte in allen Einzelzeilen gleich ist, wird ein ZUFÄLLIGER Datensatz in das Ergebnis übernommen!
Mit der Aggregatfunktion bestimmst du stattdessen, welcher Datensatz in das Ergebnis kommen soll: Das Maximum, das Minimum, die Summe, die Anzahl,...
Es gibt aber leider keine Aggregatfunktion "das Neueste", weil "Neuheit" sich eben durch "ich habe die größte Sekundenzahl seit 1970" definiert - aber dein Preis keine Sekundenzahl ist, und in seinem Feld auch kein Eintragunsdatum dabei steht, und es keine NEWEST()-Aggregatfunktion gibt, die du nutzen kannst.
Es ist also immer noch die Frage offen, warum ich trotz ORDER BY nicht an das richtige Ergebniss gelange.
Dazu hast du schon dreimal eine Antwort erhalten - aber verstanden offenbar nicht.
- Sven Rautenberg
Hallo Sven,
danke für Deine Gedult mit mir. Auch wenn ich ne etwas längere Leitung zu haben scheine ist mir dann doch aufgegangen, dass es mit einem State nicht geht. Werds dann mit 2 Abfragen umsetzen.
Wünsch Dir noch einen schönen Tag
Sebastian
Hallo Sebastian,
danke für Deine Gedult mit mir. Auch wenn ich ne etwas längere Leitung zu haben scheine ist mir dann doch aufgegangen, dass es mit einem State nicht geht.
Es geht mit einem Statement. Auch mit MySQL 4.0.24. Siehe mein letztes Posting und z.B. dieses Archivposting.
Freundliche Grüße
Vinzenz
Hallo Vinzenz,
habe mal anhand Deines Artikels den SQL-State überarbeitet. Hier ist das bisherige immer noch nicht komplett richtige State. Wieder hakt es am aktuellen Preis. Dafür sind die Joins jetzt hoffendlich besser gewählt.
SELECT
tx_sfpricetracker_lists.owner,
tx_sfpricetracker_products.producer,
tx_sfpricetracker_products.name,
tx_sfpricetracker_products.description,
tx_sfpricetracker_products.link,
tx_sfpricetracker_products.amount,
MIN(tx_sfpricetracker_prices.price) AS lowestprice,
tx_sfpricetracker_prices.price AS currentprice
FROM
tx_sfpricetracker_products
INNER JOIN (tx_sfpricetracker_products_prices_mm
INNER JOIN tx_sfpricetracker_prices ON tx_sfpricetracker_products_prices_mm.uid_foreign = tx_sfpricetracker_prices.uid
) ON tx_sfpricetracker_products.uid = tx_sfpricetracker_products_prices_mm.uid_local
INNER JOIN (tx_sfpricetracker_lists_products_mm
INNER JOIN tx_sfpricetracker_lists ON tx_sfpricetracker_lists_products_mm.uid_local = tx_sfpricetracker_lists.uid
) ON tx_sfpricetracker_products.uid = tx_sfpricetracker_lists_products_mm.uid_foreign
WHERE
tx_sfpricetracker_products.uid = 2
AND tx_sfpricetracker_lists.owner = 1
GROUP BY tx_sfpricetracker_products.uid