Hallo,

Zou er mij iemand kunnen helpen AUB.
Want ik kom er niet uit.

Ik heb een tabel "gebruikers"
vb
sleutel naam
987654321 Dirk Coppens
123456789 Test


Ik heb een tabel "gelezen" waarin user_id en een news_id in staat
vb :
User_id news_id
123456789 5
123456789 9
987654321 9
987654321 3

Ik heb nog een tabel met news
vb:

ID Title
2 Libya Gingerly Begins Seeking Economic but Not Pol...
3 Pressed by U.S., Pakistan Seizes a Taliban Chief
4 Francis, Still Hobbled, Will Give It Another Try
5 Most Support U.S. Guarantee of Health Care
6 Google Courts Small YouTube Deals, and Very Soon
7 A Laptop With Vista That Seems Just Like a Fully U
9 Opportunists in Somalia


Wat is nu mijn probleem.
In tabel gelezen staan de gelezen berichten per gebruiker.
Ik wil enkel de records tonen van een gebruiker die nog niet gelezen zijn.

Alvast bedankt

Ongelezen items staan dus niet in de tabel.
ikzelf zou dan denken aan zoiets:

SELECT ID, Title FROM news WHERE ID NOT IN (SELECT news_id FROM gelezen)
Dan kan je kijken of iets in de tabel juist niet voorkomt.
Ik heb er even geen benchmarks op los gelaten, maar volgens mij is een exists() efficiënter (en dus sneller).

select ID,Title
from news n
where not exists (select 1 from gelezen where user_id = :user and news_id = n.ID)

"Selecteer alle berichten die nog niet gelezen zijn door deze gebruiker"
Die (NOT) EXISTS variant is waarschijnlijk sneller inderdaad.

En als je de variant van @Ariën gebruikt moet je hier nog even een user toevoegen in de subquery:
SELECT ID, Title FROM news WHERE ID NOT IN (SELECT news_id FROM gelezen WHERE User_id = <user id>)

Anders worden alle gelezen berichten (door wie dan ook) er uitgefilterd.
Waarschijnlijk is een LEFT JOIN sneller op de lange duur (vooral als je meer users en nieuws krijgt):

SELECT n.ID, n.Title FROM news n
LEFT JOIN gelezen g ON g.user_id = :user AND g.news_id = n.ID
WHERE g.user_id IS NULL
Hm, als ik dit https://explainextended.com/2009/09/18/not-in-vs-not-exists-vs-left-join-is-null-mysql/ zo lees zou de not-exists juist de sloomste van de 3 moeten zijn. Als ik zelf wat testjes doe (MariaDB) merk ik daar weinig van (alle 3 even snel). Zowel de not-exists als not-in komen ook met exact hetzelfde explain plan (met een MATERIALIZED table - die hadden ze in 2009 volgens mij nog niet).

Als ik dit https://dev.mysql.com/doc/refman/8.0/en/subquery-materialization.html zo lees, dan zou de optimizer een not-in sowieso herschrijven naar een not-exists (heuj, not-exists zou dus iets sneller moeten zijn ivm geen rewrite nodig ;-) ), maar door de MATERIALIZED table zijn beide sowieso snel(ler).

Overigens heb ik ook wel eens hele slechte ervaringen met een MATERIALIZED table, waarbij het opbouwen van die temp table veel meer tijd kost dan het doen van een paar lookups voor een exists (grote tabellen met een goede index). De optimizer zover krijgen om niet te materializen is altijd weer gedoe ...
De snelheid is afhankelijk van het aantal records dat doorzocht moet worden. Misschien moet je test set gewoon wat groter? :)
Hallo

Aan allen hier een dikke merci voor de hulp.
Het is mij gelukt om mijn listing te tonen zoals ik het wilde.
Nogmaals hartelijk bedankt aan de professionals hier.

Ik heb gekozen voor :

SELECT n.ID, n.Title FROM news n
LEFT JOIN gelezen g ON g.user_id = :user AND g.news_id = n.ID
WHERE g.user_id IS NULL
U vraagt, wij draaien: twee tabellen met in tabel "news" 1,3M records, en in tabel "gelezen" 2,4M, waarvan 1,2M voor de specifieke user (andere data, maar vergelijkbare situatie). Tevens ook nog twee servers: "lokaal" = MySQL 5.7.7 (zonder MATERIALIZED kung-fu), "live" = MariaDB 10.0.34 (met).

not-exists:
- lokaal 0,110s (DEPENDENT SUBQUERY)
- live 20,8960 (MATERIALIZED)
met een extra "like" om MATERIALIZED te voorkomen (...)
- live 0,0902 (DEPENDENT SUBQUERY)

not-in:
- lokaal 0,094 (DEPENDENT SUBQUERY)
- live 32.4543 (MATERIALIZED)
geen idee hoe ik hier van die MATERIALIZED af kom ...

left-join:
- lokaal 0,094 (2x SIMPLE)
- live 0,0763 (2x SIMPLE)

Dusss, ook met een grote bak data liggen de tijden vrij dicht bij elkaar. Alleen als je de pech hebt dat de "optimizer" tot een MATERIALIZED temp table besluit kun je heel erg nat gaan (uiteraard heeft het soms ook wel voordelen). Kortom: altijd je explain plan checken, en zo af en toe eens in het "Slow Query Log" kijken (of gewoon zelf de duur van een query loggen).
Hallo,

Te vroeg viktorie geroepen ??
Als de gebruiker nog niet in de tabel gelezen staat krijg je een error ?

Met
SELECT n.ID, n.Title FROM news n
LEFT JOIN gelezen g ON g.user_id = :user AND g.news_id = n.ID
WHERE g.user_id IS NULL

Dacht dat het zou lukken maar is niet waar.

Ps de gebruiker komt maar in de tabel "gelezen als hij eentje aanduid dat hij gelezen heeft.
anders moet hij alles zien.



[size=xsmall]Toevoeging op 01/05/2018 09:48:20:[/size]

Hallo,

Heb het zelf opgelost met eerst naar het bestand lezen te gaan kijken en pas deze query uit te voeren als gebruiker er in staat.

Bedankt voor alles.

Die LEFT JOIN zou precies moeten doen wat je wil, tenzij de structuur niet is die je zegt dat hij is:
Knoop berichten en gelezen aan elkaar en toon alles wat niet in gelezen voorkomt door de gebruiker. Lijkt me toch exact te kloppen.

Reageren