Temporary table voor zoekfunctie

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Erwin H

Erwin H

12/05/2014 15:03:13
Quote Anchor link
Voor een zoekfunctie in mijn database loop ik tegen het probleem aan dat ik eigenlijk in 1 zoekopdracht, op meerdere gegevens wil zoeken. Primair is de zoektocht van gegevens uit 1 tabel op een andere tabel en in feite kan dat via joins. Probleem is echter dat de gegevens waar ik op wil zoeken niet allemaal in de database staan.

Het gaat erom dat de gebruiker eigen gegevens in de database heeft staan en die moeten gematcht worden met gegevens die door het systeem al zijn gedefinieerd. Het gaat overigens om vliegtuig registraties, om het misschien iets beter begrijpbaar te maken. Omdat de gebruiker zijn eigen fomaat kan hanteren kan het zijn dat bepaalde gegevens, ook al zouden ze gelijk moeten zijn, dat toch niet zijn. Voorbeeld: Een Belgische F-16 staat in het systeem als FA-01, maar de gebruiker kan ook FA 01, FA01 of FA-1 invullen, dit moet allemaal tot een match leiden met FA-01. Om dat te doen is er een extra tabel met een 'opgeschoonde' waarde voor de systeem gegevens. Daar zal deze registratie als FA1 instaan wat is bepaald via een regex en andere regels.

Het probleem is nu dat ik de gebruiker laat zoeken op zijn eigen data in de database, maar voor de zoektocht start zal ik eerst van alle door de gebruiker ingevoerde gegevens de opgeschoonde waarde moeten bepalen. Die staat niet in de database, die bepaal ik dus op het moment dat het nodig is. Waarop ik dan zoek is het ID en de zojuist bepaalde opgeschoonde waarde. Zoek ik op 1 record dan is dat geen probleem (versimpeld):

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
SELECT DISTINCT aa.plane_id, aa.registration
FROM (
  (SELECT a.plane_id, m.registration,
   FROM planes a
   INNER JOIN planes m ON (
     (m.registration = a.registration OR m.registration = 'FA1')
     AND m.plane_id <> a.plane_id
   )
   WHERE (m.country_id = a.country_id OR a.country_id = 0)
     AND a.plane_id = 1)
  UNION
  (SELECT a.plane_id, mp.registration,
   FROM planes a
   INNER JOIN registration_search ms ON (
     (ms.search_value = a.registration OR ms.search_value = 'FA1')
     AND ms.plane_id <> a.plane_id
   )
   INNER JOIN planes mp ON ms.plane_id = mp.plane_id

   WHERE (mp.country_id = a.country_id OR a.country_id = 0)
     AND a.plane_id = 1)
) aa
ORDER BY aa.plane_id, aa.registration;

De waarde 'FA1' is dus de opgeschoonde waarde, en in dit voorbeeld hoort dat bij het record met id 1.
Wil ik nu echter op id 1 en id 2 zoeken dan kan dat niet meer op deze manier, tenzij ik nog twee SELECTs maak via de UNION. Ik wil echter op 100 records tegelijk kunnen zoeken, wat tot 200 SELECTs zou leiden....

Vraag is voor mij, wat kan wel. Een temporary table komt in me op, omdat ik dan alle berekende waardes in een tabel heb staan en dan werkt het vrij eenvoudig via joins. Dan kan ik op zoveel records zoeken als ik wil. Mijn kennis van temporary tables is echter beperkt en ik weet dus niet wat daar de voorwaarden en nadelen van zijn. Hoe zit het bijvoorbeeld met naamgeving (om te voorkomen dat er twee users tegelijk zoeken en elkaar in de weg zitten), hoe zit het met performance, etc. Iemand die daar iets op kan zeggen, bij voorkeur in combinatie met het voorbeeld? Eventuele andere oplossingen zijn overigens ook zeer welkom!
Gewijzigd op 12/05/2014 19:04:22 door Erwin H
 
PHP hulp

PHP hulp

17/11/2024 01:45:17
 
Ward van der Put
Moderator

Ward van der Put

12/05/2014 16:09:47
Quote Anchor link
>> Wil ik nu echter op id 1 en id 2 zoeken dan kan dat niet meer op deze manier, tenzij ik nog twee SELECTs maak via de UNION. Ik wil echter op 100 records tegelijk kunnen zoeken, wat tot 200 SELECTs zou leiden....

Niet met WHERE a.plane_id = 1 inderdaad, maar wel met WHERE a.plane_id IN (...). Is dat geen oplossing, eventueel met een subquery?
 
Erwin H

Erwin H

12/05/2014 16:34:23
Quote Anchor link
Nee, dat is geen oplossing, omdat ik dus op twee verschillende velden zoek, die bij elkaar horen. Als ik WHERE a.plane_id IN (1,2) doe, dan moet ik ook in de join voorwaarde de meerdere waardes opgeven: OR m.registration IN ('FA1','FA2'). Dan krijg ik echter ook matches voor plane_id = 2 bij registration = 'FA1'. En die horen niet bij elkaar.
 
Ger van Steenderen
Tutorial mod

Ger van Steenderen

12/05/2014 17:32:44
Quote Anchor link
Het nadeel van temporary tabels is dat je ze eerst moet aanmaken en vullen.
Naamgeving is niet het probleem, dat werkt per connectie (wordt intern bijgehouden).
Voordeel is dat het (buiten het temporary) normale tabellen zijn.

>> Probleem is echter dat de gegevens waar ik op wil zoeken niet allemaal in de database staan
Jou kennende zal je daar wel een reden voor hebben, dus vraag ik hem maar ;-)
Gewijzigd op 12/05/2014 17:33:08 door Ger van Steenderen
 
Erwin H

Erwin H

12/05/2014 17:46:02
Quote Anchor link
Precies daarom ben ik ook een beetje terughoudend om die temporary tables te gebruiken. Op zich is dit geen functie die te pas en te onpas gebruikt zal gaan worden, maar toch...

Er is inderdaad een reden voor :-)
Bij de systeemgegevens staat het in de database omdat er regelmatig op gezocht wordt. Bij de gebruikers is dat niet zo. Alleen als er een record wordt toegevoegd is het nodig, maar dan maar eenmalig en verder niet in de situatie als hierboven waardoor het als losse parameter kan worden gebruikt. Het enige andere moment waarop het nodig kan zijn is als de besproken zoek functie gebruikt wordt. In principe zal dat maar 1 keer zijn per record, in de meeste gevallen zelfs nooit. Als bij invoer de juiste link namelijk al is gelegd dan zal er nooit meer op gezocht hoeven te worden.

Het opslaan van deze opgeschoonde data is dus nutteloos, behoudens die ene mogelijke keer dat er op gezocht moet worden.

Toevoeging op 12/05/2014 18:43:24:

Handig.... ik heb de temporary table nodig in beide selects van de UNION. En dat vindt MySQL niet leuk "Can't reopen table...". Het blijkt dat MySQL niet in staat is om meer dan 1 keer in dezelfde query naar een temporary table te verwijzen. Dus nu heb ik al twee temporary tables nodig.....

Bug hier: http://bugs.mysql.com/bug.php?id=10327
 
Ger van Steenderen
Tutorial mod

Ger van Steenderen

12/05/2014 18:50:22
Quote Anchor link
Wat staat er in tabel registration_search?
Ik kan die self-join in de eerste select ook niet helemaal plaatsen.
 
Erwin H

Erwin H

12/05/2014 19:03:43
Quote Anchor link
In registration_search staan de opgeschoonde gegevens van de records in het systeem. Dat is een aparte tabel omdat het niet in alle gevallen nodig is. Een registratie '1' bijvoorbeeld zal altijd '1' blijven. Uit normalisatie overwegingen dus een aparte tabel.

De self join is nodig omdat ik gegevens uit de tabel aan het vergelijken ben met gegevens in andere rijen uit dezelfde tabel. Ik wil dus weten of er rijen zijn waarvoor de registratie gelijk is aan de registratie van de rij die gebruiker heeft opgegeven, of waarvan de registratie gelijk is aan de opgeschoonde versie van de registratie die de gebruiker ooit heeft ingevoerd. In de join filter ik daarbij al de rij zelf, want in die rij ben ik niet geinteresseerd.

(er stond alleen nog een foute alias ergens, die is verbeterd)
 
Ger van Steenderen
Tutorial mod

Ger van Steenderen

12/05/2014 19:28:52
Quote Anchor link
Ik probeer even wat inzicht te krijgen zodat ik met je mee kan (proberen te) denken. Ik zie 5 keer dezelfde tabel in de query, volgens mij moet dat eenvoudiger kunnen.

- plane_id staat voor bv een F-16 en is niet unique in planes?
- in planes.registration kunnen dan ook FA-1, FA01 etc. voorkomen?
- in de tabel registration_search staan dan de afwijkende waardes met de officiele waarde?
 
Erwin H

Erwin H

12/05/2014 19:39:30
Quote Anchor link
Alles is welkom, ik zal proberen het uit te leggen. Probleem is wel dat ik het iets versimpeld heb (voor dit topic), dus het kan ergens op een gegeven moment mislopen.

- plane_id is de unieke sleutel van de tabel en staat dus niet voor het type.
- in registraation kan inderdaad vanalles voorkomen. In principe is er een record aanwezig waarin de 'officiele' versie staat die door een admin in het systeem is gezet. Door verschillende manieren om het op te schrijven kan er door een gebruiker echter een nieuw record aangemaakt zijn wat in feite dezelfde kist is. Het kan dus zo zijn dat de officiele versie 'FA-01' is, maar dat er ook ergens een record te vinden is met 'FA01', of 'FA 01'.
- in de tabel registration_search staan de opgeschoonde waardes van alleen de officiele records. Dus voor het record met 'FA-01' zal er een search waarde 'FA1' zijn. Als dus een gebruiker nu 'FA 01' intikt en zoekt naar een match dan zal die opgeschoonde waarde van 'FA 01' (wat 'FA1' is) matchen met het officiele record en zo kan de correcte link gelegd worden.
 
Ger van Steenderen
Tutorial mod

Ger van Steenderen

12/05/2014 19:56:42
Quote Anchor link
>>Probleem is wel dat ik het iets versimpeld heb (voor dit topic), dus het kan ergens op een gegeven moment mislopen.

Misschien heb je het wel te veel versimpeld:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
SELECT a.plane_id, m.registration,
   FROM planes a
   INNER JOIN planes m ON (
     (m.registration = a.registration OR m.registration = 'FA1')
     AND m.plane_id <> a.plane_id
   )
   WHERE (m.country_id = a.country_id OR a.country_id = 0)
     AND a.plane_id = 1)

Is hetzelfde als
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
SELECT plane_id, registration
FROM planes
WHERE plane_id = 1

Of mis ik iets?
Gewijzigd op 12/05/2014 20:17:24 door Ger van Steenderen
 
Erwin H

Erwin H

12/05/2014 21:43:26
Quote Anchor link
Het zou best kunnen dat ik net een stap te ver ben gegaan in het versimpelen hoor, maar een hier een voorbeeld dat niet gelijke resultaten zal weergeven in de twee queries:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
plane_id  |  registration  |  country_id
----------+----------------+--------------
 1        |  FA01          |  0
 2        |  FA-01         |  1

resultaat mijn query:
geen

resultaat jouw query:
plane_id: 1
registration: FA01


Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
plane_id  |  registration  |  country_id
----------+----------------+--------------
 3        |  37+01         |  0
 4        |  3701          |  1


resultaat mijn query:
plane_id: 3
registration: 3701

resultaat jouw query:
plane_id: 3
registration: 37+01

(merk op dat in dit geval de opgeschoonde registratie 3701 zou zijn, in plaats van FA1 dus)
Gewijzigd op 12/05/2014 21:45:24 door Erwin H
 
Ger van Steenderen
Tutorial mod

Ger van Steenderen

13/05/2014 10:20:47
Quote Anchor link
Je hebt gelijk, ik zag die tweede join voorwaarde over het hoofd.

Erwin H op 12/05/2014 19:39:30:

- in de tabel registration_search staan de opgeschoonde waardes van alleen de officiele records. Dus voor het record met 'FA-01' zal er een search waarde 'FA1' zijn. Als dus een gebruiker nu 'FA 01' intikt en zoekt naar een match dan zal die opgeschoonde waarde van 'FA 01' (wat 'FA1' is) matchen met het officiele record en zo kan de correcte link gelegd worden.


Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
   INNER JOIN registration_search ms ON (
     (ms.search_value = a.registration OR ms.search_value = 'FA1')
     AND ms.plane_id <> a.plane_id
   )
   INNER JOIN planes mp ON ms.plane_id = mp.plane_id


Hoe staat dan uit jou voorbeeld plane_id 3 in registration_search?
 
Erwin H

Erwin H

13/05/2014 10:46:29
Quote Anchor link
plane_id 1 en 3 staan er niet in. Dat zijn door gebruikers aangemaakte records en die worden dus niet opgevoerd in de search tabel omdat ze niet gevonden hoeven te worden.

En voor de goede orde, ik heb inmiddels in een test omgeving de situatie met twee temporary tables draaien. Het werkt dus inmiddels wel, maar ik blijf me afvragen of het de beste oplossing is....
 
Ger van Steenderen
Tutorial mod

Ger van Steenderen

13/05/2014 19:11:00
Quote Anchor link
Zonder een klein beetje denormaliseren is er geen betere oplossing.

Je zou eventueel in SQL nog een functie kunnen maken voor het opschonen van de data, maar dat levert volgens mij ook weinig voordeel op.
 
Erwin H

Erwin H

14/05/2014 11:47:19
Quote Anchor link
Het opschonen van de data in SQL is, denk ik, geen optie, gezien de complexiteit ervan. Denormaliseren zou inderdaad wel een optie zijn, maar dat wil ik niet. Dus.... dan moet ik het zo houden. Op het moment ben ik ermee aan het testen en het werkt in de test omgeving prima, zonder merkbare vertraging. Uiteraard is in de test omgeving de database wel wat kleiner, dus we zullen gaan zien hoe het in het echt gaat werken....

In elk geval weer eens met wat nieuws wezen stoeien, dat is altijd al meteen een voordeel :-)

Dank voor je hulp!
 
Ger van Steenderen
Tutorial mod

Ger van Steenderen

14/05/2014 12:23:57
Quote Anchor link
Is het dan zo erg om een record voor elk vliegtuig bij te houden met de opgeschoonde waarde?
 
Erwin H

Erwin H

14/05/2014 12:40:52
Quote Anchor link
Nee, op zich niet. Het is mijn keuze om dat niet te doen. Een keuze tussen:
1) extra (berekende) gegevens opslaan die niet of nauwelijks nodig zijn, maar als het nodig is dan wel iets sneller de zoekopdracht vervullen
2) minder data opslag, maar een wat complexere zoekopdracht

Ik kies voor 2, maar ik wil niet zeggen dat keuze 1 dus fout of slecht is. Het is mijn inschatting dat hoewel de zoekopdracht nu wat complexer is, het me uiteindelijk rekenkracht en opslag scheelt, omdat ik nu ietwat minder complexe inserts en updates hoef uit te voeren. Maar of het echt quantificeerbaar is is zeer de vraag.
 



Overzicht Reageren

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.