SQL - Hämta poster där e-postadressen ej förekommer tidigare

Permalänk
Medlem

SQL - Hämta poster där e-postadressen ej förekommer tidigare

Hej,

Jag försöker skriva en SQL-fråga för att beräkna följande

a) Räkna hur många ordrar, lagda under ett visst intervall, som kommer från en ny kund (ny e-postadress)
b) Räkna hur många ordrar, lagda under ett visst intervall, som kommer från en återkommande kund (e-postadressen finns redan i order-databasen)

Jag kommer så långt att jag har följande SQL-frågor som fungerar fint om jag skiter i tidsintervallet.

a) SELECT count(*) FROM (SELECT * FROM OrderTable WHERE approveDate>0 GROUP BY buyerEmail HAVING COUNT(buyerEmail)=1) as xxx; b) SELECT count(*) FROM (SELECT * FROM OrderTable WHERE approveDate>0 GROUP BY buyerEmail HAVING COUNT(buyerEmail)>1) as xxx;

Dessa fungerar bra.

Men när jag vill lägga in ett tidsintervall för när ordern ska vara lagd skiter det sig. Jag vill t.ex. kunna se hur många av januaris kunder som var nya, resp. återkommande.

Problemet är att när jag lägger till ett tidsintervall för köpet, så exkluderas ju även e-postadresser utanför det givna intervallet. Dvs återkommande kunder "räknas" som nya.

Exempel på ej fungerande försök

SELECT count(*) FROM (SELECT * FROM OrderTable WHERE approveDate>2011-01-01 AND approveDate<2011-01-10 GROUP BY buyerEmail HAVING COUNT(buyerEmail)=1) as xxx;

Någon som kan puffa mig i rätt riktning? Hur ställer jag datumfrågan så att den endast gäller "senaste köp" men e-postadressen får vara från valfritt datum?

Visa signatur

Modermodem från ONOFF och bildskärmsutrustning från Electrolux. US Robotics 28.8K telefonuppringd internetförbindelse. Har en förkärlek för tysk tjejpop.

Permalänk
Medlem

SQL keywordet 'IN' borde väl passa rätt bra här.

a) SELECT COUNT(*) FROM OrderTable WHERE approveDate>2011-01-01 AND approveDate<2011-01-10 AND buyerEmail NOT IN (SELECT buyerEmail FROM OrderTable) as t; a) SELECT COUNT(*) FROM OrderTable WHERE approveDate>2011-01-01 AND approveDate<2011-01-10 AND buyerEmail IN (SELECT buyerEmail FROM OrderTable) as t;

Visa signatur

The difference between stupidity and genius - the latter has limits

Permalänk
Medlem
Skrivet av Zevon:

SQL keywordet 'IN' borde väl passa rätt bra här.

a) SELECT COUNT(*) FROM OrderTable WHERE approveDate>2011-01-01 AND approveDate<2011-01-10 AND buyerEmail NOT IN (SELECT buyerEmail FROM OrderTable) as t; a) SELECT COUNT(*) FROM OrderTable WHERE approveDate>2011-01-01 AND approveDate<2011-01-10 AND buyerEmail IN (SELECT buyerEmail FROM OrderTable) as t;

Tack för bra svar!
Funderade faktiskt på det själv, men provade först nu.

Fungerar perfekt - men tyvärr sanslöst segt.
13 min 31.11 sec för att exekvera a. På lokala burken då, går sannolikt något snabbare på skarpa servern. Men knappast smidigt snabbt.

Kan jag indexera bättre kanske?

Eller någon som har tips på mer optimerad kod? 13 min är ju ruskigt tråkigt om man kör på en webbserver där man vill bläddra lite. Iofs går det att lägga ett batchjobb att skapa rapporter en gång i veckan.. men slipper man det är det ju så mycket lättare

Visa signatur

Modermodem från ONOFF och bildskärmsutrustning från Electrolux. US Robotics 28.8K telefonuppringd internetförbindelse. Har en förkärlek för tysk tjejpop.

Permalänk
Medlem

1) Har du ett index på buyerEmail kolumnen? Det känns ju som att den kört linjärsökning iom att det gått så långsamt..
CREATE INDEX ordertable_buyeremail_idx ON OrderTable (buyerEmail);

2) Du kan ju testa köra DISTINCT(buyerEmail) i subqueryn.. Blir ju ett mindre dataset i subqueryn, men den måste ju dock då beräknas innan användning, så gissningsvis bir det inte någon prestandavinst.

3) Du kan överväga att bryta ut buyerEmail till en egen tabell (som sedan då länkas ihop av OrderTable med en foreign key). Fördelen skulle ju isf vara att mail kolumnen skulle kunna vara UNIQUE..
Om det är viktigt att OrderTable ser ut exakt som den gör nu skulle du isf kunna wrappa den via en VIEW..

EDIT:
Förresten, du säger att det fungerar perfekt men att det är segt.. Men fungerar det verkligen?
Tycker mig ana att det inte borde fungera alls..

Matchar inte subqueryn alltid mot sig själv? (Då varje rad du jämför för kommer att ha sin mailaddress i subquery settet)
Borde väl behöva en WHERE som excluderar den egna raden, så typ:

SELECT COUNT(*) FROM OrderTable WHERE approveDate>2011-01-01 AND approveDate<2011-01-10 AND buyerEmail IN (SELECT buyerEmail FROM OrderTable as t WHERE OrderTable.id != t.id) as t2;

Och måste ändå köra sånna där conditions så kan man väl lika gärna köra på din variant, borde väl vara rätt mycket snabbare då subqueryn blir statisk för alla rader, istället för som ovan då IN-queryn blir unik för alla (en bra optimerara lär väl kunna fixa en hel del iof))

a) SELECT count(*) FROM (SELECT COUNT(*) as c FROM OrderTable WHERE approveDate>2011-01-01 AND approveDate<2011-01-10 GROUP BY buyerEmai) as t WHERE t.c=1; b) SELECT count(*) FROM (SELECT COUNT(*) as c FROM OrderTable WHERE approveDate>2011-01-01 AND approveDate<2011-01-10 GROUP BY buyerEmai) as t WHERE t.c>1;

EDIT2:
Nä vänta, det där har ju samma problem som du haft tidigare.. Testa detta istället

a) SELECT count(*) FROM OrderTable JOIN (SELECT buyerEmail,COUNT(*) as c FROM OrderTable GROUP BY buyerEmail) as t ON t.buyerEmail=buyerEmail WHERE t.c=1 AND approveDate>2011-01-01 AND approveDate<2011-01-10;

Så lite sanity-check.
Hämtar en lista av buyerEmail och förekomster, joinar med OrderTable.
OrderTable har nu kolumnen c som betäcknar hur många ordrar kunden har..
En rad inom ett visst approveDate interval är därmed för en ny kund om c=1, och för en återkommande kund om c>1.
Verkar stämma tycker jag.

Då du joinar på buyerEmail så är det ju trevligt om du kör den indexerat..

Tredje gången gillt?

Visa signatur

The difference between stupidity and genius - the latter has limits

Permalänk
Medlem

SELECT max(approveDate) as latest_date, count(*) AS orders, buyerEmail FROM OrderTable GROUP BY buyerEmail HAVING orders = 1 AND latest_date BETWEEN '2011-04-01' AND '2011-05-01'

med index på approveDate och kanske buyerEmail.