Ik zou proberen dit VOLLEDIG te vereenvoudigen door triggers op je andere tabellen te plaatsen en gewoon een paar kolommen toe te voegen aan je User_Fans-tabel ... Eén voor elke respectieve telling () die je probeert te krijgen ... van Posts, PostLikes, PostComments, PostCommentVind ik leuk.
Wanneer een record wordt toegevoegd aan welke tabel dan ook, werk dan gewoon uw user_fans tabel bij om 1 toe te voegen aan de telling... het zal hoe dan ook vrijwel onmiddellijk zijn op basis van de sleutel-ID van de gebruiker. Wat betreft de "LIKES"... Gelijkaardig, alleen onder de voorwaarde dat iets wordt geactiveerd als een "Vind ik leuk", voeg 1 toe. Dan zal uw query een directe wiskunde zijn op het enkele record en niet vertrouwen op ENIGE joins om een totale waarde "gewogen". Naarmate uw tabel nog groter wordt, worden de query's ook langer omdat ze meer gegevens hebben om door te stromen en te aggregeren. Je doorloopt ELKE user_fan-record die in wezen elke record van alle andere tabellen opvraagt.
Dat gezegd hebbende, zou ik de tabellen als volgt herstructureren...
SELECT
uf.user_name,
uf.user_id,
@pc := coalesce( PostSummary.PostCount, 000000 ) as PostCount,
@pl := coalesce( PostLikes.LikesCount, 000000 ) as PostLikes,
@cc := coalesce( CommentSummary.CommentsCount, 000000 ) as PostComments,
@cl := coalesce( CommentLikes.LikesCount, 000000 ) as CommentLikes,
@pc + @cc AS sum_post,
@pl + @cl AS sum_like,
@pCalc := (@pc + @cc) * 10 AS post_cal,
@lCalc := (@pl + @cl) * 5 AS like_cal,
@pCalc + @lCalc AS `total`
FROM
( select @pc := 0,
@pl := 0,
@cc := 0,
@cl := 0,
@pCalc := 0
@lCalc := 0 ) sqlvars,
user_fans uf
LEFT JOIN ( select user_id, COUNT(*) as PostCount
from post
group by user_id ) as PostSummary
ON uf.user_id = PostSummary.User_ID
LEFT JOIN ( select user_id, COUNT(*) as LikesCount
from post_likes
group by user_id ) as PostLikes
ON uf.user_id = PostLikes.User_ID
LEFT JOIN ( select user_id, COUNT(*) as CommentsCount
from post_comment
group by user_id ) as CommentSummary
ON uf.user_id = CommentSummary.User_ID
LEFT JOIN ( select user_id, COUNT(*) as LikesCount
from post_comment_likes
group by user_id ) as CommentLikes
ON uf.user_id = CommentLikes.User_ID
ORDER BY
`total` DESC
LIMIT 20
My variables are abbreviated as
"@pc" = PostCount
"@pl" = PostLikes
"@cc" = CommentCount
"@cl" = CommentLike
"@pCalc" = weighted calc of post and comment count * 10 weighted value
"@lCalc" = weighted calc of post and comment likes * 5 weighted value
De LEFT JOIN naar prequery's voert die query's EENMAAL door, dan wordt het hele ding samengevoegd in plaats van als een subquery voor elke record te worden geraakt. Door COALESCE() te gebruiken, als er geen dergelijke vermeldingen zijn in de resultaten van de LEFT JOINed-tabel, krijgt u geen NULL-waarden die de berekeningen verpesten, dus ik heb ze standaard ingesteld op 000000.
VERDUIDELIJKING VAN UW VRAGEN
U kunt elke QUERY hebben als een "AS AliasResult". De "As" kan ook worden gebruikt om lange tabelnamen te vereenvoudigen voor een eenvoudigere leesbaarheid. Aliassen kunnen ook dezelfde tabel gebruiken, maar als een andere alias om vergelijkbare inhoud te krijgen, maar voor een ander doel.
select
MyAlias.SomeField
from
MySuperLongTableNameInDatabase MyAlias ...
select
c.LastName,
o.OrderAmount
from
customers c
join orders o
on c.customerID = o.customerID ...
select
PQ.SomeKey
from
( select ST.SomeKey
from SomeTable ST
where ST.SomeDate between X and Y ) as PQ
JOIN SomeOtherTable SOT
on PQ.SomeKey = SOT.SomeKey ...
Nu is de derde query hierboven niet praktisch en vereist de ( volledige query resulterend in de alias "PQ" die "PreQuery" vertegenwoordigt). Dit kan worden gedaan als u een bepaalde reeks andere complexe voorwaarden vooraf wilt beperken en een kleinere reeks wilt VOORDAT u extra joins doet met veel andere tabellen voor alle uiteindelijke resultaten.
Aangezien een "FROM" geen echte tabel hoeft te zijn, maar een query op zichzelf kan zijn, elke andere plaats die in de query wordt gebruikt, moet hij weten hoe hij naar deze prequery-resultatenset moet verwijzen.
Bij het opvragen van velden kunnen ze ook "Als FinalColumnName" zijn om de resultaten te vereenvoudigen tot waar ze ook zullen worden gebruikt.
selectCONCAT(Gebruiker.Aanhef, Gebruiker.Achternaam ) as CourtesyNamefrom ...
selectOrder.NonTaxable+ Order.Taxable+ ( Order.Taxable * Order.SalesTaxRate ) als OrderTotalWithTaxfrom ...
De kolomnaam "Als" is NIET verplicht om een aggregaat te zijn, maar wordt meestal op die manier gezien.
Nu, met betrekking tot de MySQL-variabelen... Als u een opgeslagen procedure aan het doen was, zullen veel mensen deze vooraf declareren door hun standaardwaarden in te stellen voor de rest van de procedure. U kunt ze in-line in een query uitvoeren door dat resultaat gewoon een "alias"-referentie in te stellen en te geven. Bij het doen van deze variabelen simuleert de select altijd een ENKEL RECORD met de waarde van de waarden. Het is bijna als een bijwerkbaar enkel record dat in de query wordt gebruikt. U hoeft geen specifieke "Join"-voorwaarden toe te passen, omdat dit mogelijk geen invloed heeft op de rest van de tabellen in een query... In wezen wordt een Cartesiaans resultaat gemaakt, maar één record tegen een andere tabel zal nooit een dupliceert hoe dan ook, dus geen schade stroomafwaarts.
select
...
from
( select @SomeVar := 0,
@SomeDate := curdate(),
@SomeString := "hello" ) as SQLVars
Nu, hoe de sqlvars werken. Denk aan een lineair programma... Eén opdracht wordt uitgevoerd in de exacte volgorde waarin de query wordt uitgevoerd. Die waarde wordt dan opnieuw opgeslagen in het "SQLVars"-record, klaar voor de volgende keer. U verwijst er echter niet naar als SQLVars.SomeVar of SQLVars.SomeDate... alleen de @SomeVar :=someNewValue. Wanneer de @var nu in een query wordt gebruikt, wordt deze ook opgeslagen als een "As ColumnName" in de resultatenset. Soms kan dit slechts een tijdelijke berekende waarde zijn ter voorbereiding van het volgende record. Elke waarde is dan direct beschikbaar voor de volgende rij. Dus, gezien het volgende voorbeeld...
select
@SomeVar := SomeVar * 2 as FirstVal,
@SomeVar := SomeVar * 2 as SecondVal,
@SomeVar := SomeVar * 2 as ThirdVal
from
( select @SomeVar := 1 ) sqlvars,
AnotherTable
limit 3
Will result in 3 records with the values of
FirstVal SecondVal ThirdVal
2 4 8
16 32 64
128 256 512
Merk op hoe de waarde van @SomeVar wordt gebruikt omdat elke kolom deze gebruikt... Dus zelfs voor dezelfde record is de bijgewerkte waarde onmiddellijk beschikbaar voor de volgende kolom... Dat gezegd hebbende, kijk nu eens naar het bouwen van een gesimuleerd aantal records / ranking per klant...
select
o.CustomerID,
o.OrderID
@SeqNo := if( @LastID = o.CustomerID, @SeqNo +1, 1 ) as CustomerSequence,
@LastID := o.CustomerID as PlaceHolderToSaveForNextRecordCompare
from
orders o,
( select @SeqNo := 0, @LastID := 0 ) sqlvars
order by
o.CustomerID
De "Order By"-clausule dwingt de resultaten eerst in volgorde te worden geretourneerd. Dus hier worden de records per klant geretourneerd. Eerste keer door, LastID is 0 en klant-ID is zeg...5. Omdat het anders is, retourneert het 1 als @SeqNo, DAN bewaart het die klant-ID in het veld @LastID voor de volgende record. Nu, volgende record voor klant... Laatste ID is hetzelfde, dus het neemt de @SeqNo (nu =1), en telt 1 bij 1 op en wordt #2 voor dezelfde klant... Ga verder op het pad.. .
Om beter te worden in het schrijven van query's, kijk eens naar de MySQL-tag en bekijk enkele van de zware bijdragers. Kijk in de vragen en enkele van de complexe antwoorden en hoe het oplossen van problemen werkt. Om niet te zeggen dat er geen anderen zijn met lagere reputatiescores die net beginnen en volledig competent zijn, maar je zult ontdekken wie goede antwoorden geeft en waarom. Kijk ook naar hun geschiedenis van geposte antwoorden. Hoe meer je leest en volgt, hoe beter je grip krijgt op het schrijven van complexere zoekopdrachten.