[(My)SQL] Virtuele NULL waarde voor ID kolom in query

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • egonolieux
  • Registratie: Mei 2009
  • Laatst online: 06-01-2024

egonolieux

Professionele prutser

Topicstarter
Ik heb 3 tabellen (a, b en c) waarvan ik voor elke combinatie een aantal (count) in een subquery wil berekenen:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
SELECT 
  table_a.id AS id_A,
  table_b.id AS id_b,
  table_c.id AS id_c,
  (
    SELECT
      COUNT(other_table.id)
    FROM
      other_table
    WHERE
      other_table.main_table_id = main_table.id
      AND other_table.table_a_id = table_a.id
      AND other_table.table_b_id = table_c.id
      ANDother_table.table_c_id = table_c.id
  ) AS item_count
FROM
  main_table
  INNER JOIN table_a ON (1 = 1)
  INNER JOIN table_b ON (1 = 1)
  INNER JOIN table_c ON (1 = 1)
WHERE
  main_table.id = 100


Dit werkt prima, maar nu zou ik ook "geaggregeerde waarden" willen berekenen. Daarmee bedoel ik dat de ID kolom van tabel a, b of c een NULL waarde zou krijgen. Eenvoudiger gezegd: ik wil het aantal uit de subquery weten als bijvoorbeeld a = 1, b = 3 en c ongespecifieerd is. Stel dat de c tabel in totaal 10 rijen heeft, dan zou het aantal uit de subquery "alle waarden van c" moeten opnemen. Dit zou ik ook voor alle mogelijke combinaties willen.

Is dit mogelijk binnen de context van de query? Indien niet, bestaat er dan een andere manier om wat ik wil te bereiken? Naief gezegd zou ik op een of andere manier een extra NULL ID moeten toevoegen aan de a, b en c tabellen. Vervolgens zou ik de WHERE clause van de subquery misschien kunnen herschrijven als een LEFT JOIN met een conditie op de JOIN zelf?

Beste antwoord (via egonolieux op 19-02-2018 20:10)


  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 14:02
@egonolieux Ik zou toch kijken naar wat @Vircos al voorstelde. Dus een moedertabel maken die de basis legt voor je joins incl NULLs. Onderstaande voorbeeld gaat uit van generate_series in PostgreSQL, maar generate_series is ook na te bouwen in MySQL las ik.

SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
with cte as (
    select
        NULL::int as card_variant_type,
        NULL::int as card_language_id,
        NULL::int as card_condition_id
    union all
    select
        a.*,
        b.*,
        c.*
    from 
        (select
            generate_series (
                (select min(id) from card_variant),
                (select max(id) from card_variant)
            )
        union all
            select NULL
        ) as a,
        (select
            *
        from
            generate_series (
                (select min(id) from card_language),
                (select max(id) from card_language)
            )
        union all
            select NULL
        ) as b,
        (select
            *
        from
            generate_series (
                (select min(card_condition_id) from user_has_card_variant),
                (select max(card_condition_id) from user_has_card_variant)
            )
        union all
            select NULL
        ) as c
)
select
    *
from
    cte
union
select
    *
from
    cte
order by 1, 2, 3

De laatste union is bedoeld voor een laatste ontdubbeling

card_variant_typecard_language_idcard_condition_id
111
112
113
11
121
122
123
12
11
12
13
1
211
212
213
21
221
222
223
22
21
22
23
2
311
312
313
31
321
322
323
32
31
32
33
3
11
12
13
1
21
22
23
2
1
2
3
  

Sinds de 2 dagen regel reageer ik hier niet meer

Alle reacties


Acties:
  • 0 Henk 'm!

  • DHH
  • Registratie: Augustus 2014
  • Laatst online: 07-09-2024

DHH

Wat is de uitkomst als je de subquery verandert in:

SQL:
1
2
3
4
5
6
7
8
9
10
  (
    SELECT
      COUNT(other_table.id)
    FROM
                                        other_table 
      INNER JOIN        main_table  ON  other_table.id = main_table.id
      LEFT OUTER JOIN   table_a     ON  other_table.table_a_id = table_a.id
      LEFT OUTER JOIN   table_b     ON  other_table.table_b_id = table_b.id
      LEFT OUTER JOIN   table_c     ON  other_table.table_c_id = table_c.id
  ) AS item_count


Volgens mij zou dit je resultaat moeten geven (evt. aangevuld met 'WHERE main_table_id = 100' in je subquery).

Acties:
  • 0 Henk 'm!

  • drm
  • Registratie: Februari 2001
  • Laatst online: 09-06 13:31

drm

f0pc0dert

Als ik het goed begrijp wil je alle combinaties van waarden tellen, ook als a, b, en c NULL zijn in other_table, of helemaal niet voorkomen?

Intuitief zou ik zeggen: draai het om en RIGHT JOIN de other_table met een subquery, met in de subquery de CROSS JOIN (die je nu als INNER JOIN ON 1=1 hebt geschreven). Eventueel maak je van je subquery dan een view en is het voor het nageslacht ook beter te begrijpen.

Maar ik ben er niet zeker van dat ik je helemaal begrijp. Misschien kun je je praktijk iets beter toelichten.

Music is the pleasure the human mind experiences from counting without being aware that it is counting
~ Gottfried Leibniz


Acties:
  • 0 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 09-09 13:58

NMe

Quia Ego Sic Dico.

drm schreef op vrijdag 5 januari 2018 @ 00:22:
Als ik het goed begrijp wil je alle combinaties van waarden tellen, ook als a, b, en c NULL zijn in other_table, of helemaal niet voorkomen?

Intuitief zou ik zeggen: draai het om en RIGHT JOIN de other_table met een subquery, met in de subquery de CROSS JOIN (die je nu als INNER JOIN ON 1=1 hebt geschreven). Eventueel maak je van je subquery dan een view en is het voor het nageslacht ook beter te begrijpen.

Maar ik ben er niet zeker van dat ik je helemaal begrijp. Misschien kun je je praktijk iets beter toelichten.
Volgens mij heeft DHH het goed begrepen. Ziet ernaar uit dat die hele subquery niet nodig is.

offtopic:
Was het trouwens niet zo dat in MySQL left joins beter performen dan right joins? Ik weet dat ze in MSSQL gelijke execution plans opleveren maar MySQL heeft wel gekkere quirks.

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

  • egonolieux
  • Registratie: Mei 2009
  • Laatst online: 06-01-2024

egonolieux

Professionele prutser

Topicstarter
Bedankt voor de reacties zo ver, maar ik denk dat ik het niet helemaal goed heb uitgelegd.

Stel nu dat tabel a, b en c elk 2 rijen hebben, met elk IDs 1 en 2. Met de query die ik origineel poste geeft dit als resultaat mogelijks:

ID AID BID CAantal
1115
1123
1212
1227
2118
2121
2210
2224


Wat ik bedoel met de data aggregeren met NULL waarden is het volgende (toegepast op bovenstaande tabel):

ID AID BID CAantal
nullnullnull30
nullnull115
nullnull215
null1null17
null1113
null124
null2null13
null212
null2211
1nullnull17
1null17
1null210
11null8
1115
1123
............


null + null + null aggregeert dus bijvoorbeeld alle waarden (som van 8 rijen) = 30.
Ik moet dus ergens die NULL waarde als ID voor elk van de a, b en c tabellen toe voegen binnen de query.

De cross join heb ik geschreven als 1 = 1 omdat mijn ORM cross joins niet expliciet ondersteunt ;)

Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
@egonolieux Je eerste bericht was duidelijk hoor. Je zoekt een DISTINCT of GROUP BY zoals:
SQL:
1
2
3
4
5
6
7
8
9
10
11
SELECT 
    table_a.id AS id_a,
    table_b.id AS id_b,
    table_c.id AS id_c,
    COUNT(*) AS item_count
FROM main_table
INNER JOIN other_table ON (other_table.main_table_id = main_table.id)
LEFT JOIN table_a ON (other_table.table_a_id = table_a.id)
LEFT JOIN table_b ON (other_table.table_b_id = table_b.id)
LEFT JOIN table_c ON (other_table.table_c_id = table_c.id)
GROUP BY 1, 2, 3

[ Voor 23% gewijzigd door DJMaze op 05-01-2018 03:05 ]

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • egonolieux
  • Registratie: Mei 2009
  • Laatst online: 06-01-2024

egonolieux

Professionele prutser

Topicstarter
Ik weet niet of ik volledig mee ben, maar de foreign key in table a/b/c in other_table kan niet NULL zijn, dus geeft dit eigenlijk hetzelfde resultaat als een INNER JOIN. Wat ik wil is dat er "virtueel" een extra NULL rij voor tabel a, b en c is, zodat bijvoorbeeld:

ID AID BID CAantal
1115
1123


ook "geaggregeerd" in het resultaat vermeld wordt als:

ID AID BID CAantal
11null8 (5 + 3)


Er zijn wel degelijk rijen voor tabel c; wat ik wil is dat een NULL waarde voor een specifieke ID de waarden uit alle andere rijen aggregeert. Dit zou ik willen voor elk mogelijke combinatie, zoals ik in mijn vorige post in de laatste tabel toon.

Binnen dit voorbeeld zou je kunnen zeggen "waarom tel je ze gewoon niet buiten de database op?". Ik heb achteraf gezien een slechte query als voorbeeld gegeven; in mijn echte query zijn er 2 subqueries die elk een hele boel joins op andere tabellen doen. Wat ik eigenlijk bereken is een verhouding (aantal dat de gebruiker heeft / mogelijk aantal) voor elk mogelijke combinatie van de ID van de a, b en c tabellen.

Hierbij is het mogelijk dat de gebruiker voor combinatie 1, 1, 1 alle 100/100 heeft en voor de combinatie 1, 1, 2 25/100. Ze eenvoudigweg optellen gaat dus eigenlijk niet zoals ik in mijn vereenvoudigd voorbeeld doe. Ik wil eventueel mijn datamodel en de werkelijke query posten als jullie het nodig vinden/het nog niet helemaal duidelijk is.

[ Voor 31% gewijzigd door egonolieux op 05-01-2018 13:11 ]


Acties:
  • 0 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 09-09 13:58

NMe

Quia Ego Sic Dico.

Dat klinkt als iets dat eerder in je software past dan in je database.

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

  • egonolieux
  • Registratie: Mei 2009
  • Laatst online: 06-01-2024

egonolieux

Professionele prutser

Topicstarter
NMe schreef op vrijdag 5 januari 2018 @ 13:08:
Dat klinkt als iets dat eerder in je software past dan in je database.
Je reageert net op hetzelfde tijdstip dat ik mijn post heb aangepast en uitleg waarom dit niet gaat :p

Acties:
  • +1 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 09-09 13:58

NMe

Quia Ego Sic Dico.

egonolieux schreef op vrijdag 5 januari 2018 @ 13:09:
[...]

Je reageert net op hetzelfde tijdstip dat ik mijn post heb aangepast en uitleg waarom dit niet gaat :p
Nee, je uitleg legt juist uit waarom dat wél een goed idee is. Juist als je query al heel complex is gaat dit je niet makkelijk lukken in de query zelf. Je wil conditioneel al dan niet groeperen op alles op het moment dat één, twee of drie waarden al dan niet null zijn. Het is al snel verstandiger om alle data met een inner join op te halen en een arraytje in je software bij te houden waarin je de gegroepeerde waardes ook opneemt.

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • +1 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
@egonolieux Dus je bedoelt meer:
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
SELECT 
    table_a.id AS id_a,
    table_b.id AS id_b,
    table_c.id AS id_c,
    COUNT(*) AS item_count
FROM main_table
INNER JOIN other_table ON (other_table.main_table_id = main_table.id)
LEFT JOIN table_a ON (other_table.table_a_id = table_a.id)
LEFT JOIN table_b ON (other_table.table_b_id = table_b.id)
LEFT JOIN table_c ON (other_table.table_c_id = table_c.id)
GROUP BY 1, 2, 3

UNION SELECT 
    table_a.id AS id_a,
    table_b.id AS id_b,
    NULL AS id_c, /* virtueel */
    COUNT(*) AS item_count
FROM main_table
INNER JOIN other_table ON (other_table.main_table_id = main_table.id)
LEFT JOIN table_a ON (other_table.table_a_id = table_a.id)
LEFT JOIN table_b ON (other_table.table_b_id = table_b.id)
GROUP BY 1, 2, 3

[ Voor 6% gewijzigd door DJMaze op 05-01-2018 13:22 ]

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • egonolieux
  • Registratie: Mei 2009
  • Laatst online: 06-01-2024

egonolieux

Professionele prutser

Topicstarter
NMe schreef op vrijdag 5 januari 2018 @ 13:12:
[...]

Nee, je uitleg legt juist uit waarom dat wél een goed idee is. Juist als je query al heel complex is gaat dit je niet makkelijk lukken in de query zelf. Je wil conditioneel al dan niet groeperen op alles op het moment dat één, twee of drie waarden al dan niet null zijn. Het is al snel verstandiger om alle data met een inner join op te halen en een arraytje in je software bij te houden waarin je de gegroepeerde waardes ook opneemt.
Nog een belangrijk detail dat ik in mijn edit moest vermelden:

Het is mogelijk dat het max mogelijke aantal per combinatie varieert. Het volgende is dus mogelijk:

1 1 null => max mogelijk 100
1 1 1 => max mogelijk = 70
1 1 2 => max mogelijk = 60
1 1 3 => max mogelijk = 50
1 1 4 => max mogelijk = 25
1 1 5 => max mogelijk = 10

Naar mijn weten kan ik hier niet (eenvoudig) de geaggregeerde waarden uit afleiden, wat wel mogelijk is indien de max mogelijke aantallen allemaal 100 zouden zijn.

Ik was bij het posten van dit topic er redelijk zeker van dat dit met SQL moeilijk ging gaan, maar wou het toch even vragen. Mijn excuses dat ik deze details vergeten vermelden ben, maar ik was zodanig gefocust op die NULL waarden in het resultaat krijgen dat ik de complexiteit even over het hoofd zag ;)

[ Voor 7% gewijzigd door egonolieux op 05-01-2018 13:24 ]


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
egonolieux schreef op vrijdag 5 januari 2018 @ 12:51:
Ik heb achteraf gezien een slechte query als voorbeeld gegeven; in mijn echte query zijn er 2 subqueries die elk een hele boel joins op andere tabellen doen.
Als je die in een VIEW zet kan je even makkelijk testen met mijn vorige UNION post

Maak je niet druk, dat doet de compressor maar


Acties:
  • +1 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 09-09 13:58

NMe

Quia Ego Sic Dico.

DJMaze schreef op vrijdag 5 januari 2018 @ 13:22:
@egonolieux Dus je bedoelt meer:
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
SELECT 
    table_a.id AS id_a,
    table_b.id AS id_b,
    table_c.id AS id_c,
    COUNT(*) AS item_count
FROM main_table
INNER JOIN other_table ON (other_table.main_table_id = main_table.id)
LEFT JOIN table_a ON (other_table.table_a_id = table_a.id)
LEFT JOIN table_b ON (other_table.table_b_id = table_b.id)
LEFT JOIN table_c ON (other_table.table_c_id = table_c.id)
GROUP BY 1, 2, 3

UNION SELECT 
    table_a.id AS id_a,
    table_b.id AS id_b,
    NULL AS id_c, /* virtueel */
    COUNT(*) AS item_count
FROM main_table
INNER JOIN other_table ON (other_table.main_table_id = main_table.id)
LEFT JOIN table_a ON (other_table.table_a_id = table_a.id)
LEFT JOIN table_b ON (other_table.table_b_id = table_b.id)
GROUP BY 1, 2, 3
Volgens mij is dat inderdaad wat hij bedoelt, maar als elk van die ID's afzonderlijk NULL kan zijn (zie egonolieux in "\[(My)SQL] Virtuele NULL waarde voor ID kolom in query") heb je zes unions nodig op een query die volgens de TS al aardig complex is. Bij nog een vierde tabel worden dat 15 unions. Het is stukken eenvoudiger en zeer waarschijnlijk ook veel performanter om dat in code op te lossen.

[ Voor 4% gewijzigd door NMe op 05-01-2018 13:27 ]

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
NMe schreef op vrijdag 5 januari 2018 @ 13:25:
Het is stukken eenvoudiger en zeer waarschijnlijk ook veel performanter om dat in code op te lossen.
Daar heb je zeker gelijk in, gewoon vogelen met elke fetch_row().
Ook al kan je bijna alles oplossen met een query, performance blijft benchmarken.

Zo had ik het een keer andersom. C++ en een query met UNION bleek 50ms te duren, terwijl C++ met 4 queries er 4 seconden over deed.

[ Voor 55% gewijzigd door DJMaze op 05-01-2018 13:36 ]

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 09-09 13:58

NMe

Quia Ego Sic Dico.

DJMaze schreef op vrijdag 5 januari 2018 @ 13:30:
[...]

Zo had ik het een keer andersom. C++ en een query met UNION bleek 50ms te duren, terwijl C++ met 4 queries er 20 seconden over deed.
Dat kan inderdaad, maar hier is het gewoon één query (die met inner joins, waarschijnlijk) waarbij je in code wat extra referenties en getalletjes bijhoudt. Dat moet al heel gek lopen (of een idioot grote dataset zijn) voordat het een performance-hit wordt. :)

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
NMe schreef op vrijdag 5 januari 2018 @ 13:36:
Dat moet al heel gek lopen (of een idioot grote dataset zijn) voordat het een performance-hit wordt. :)
Hang er vanaf in welke taal hij programmeert.
C met een mulri-array heap memory table die moet groeien.... 8)7

code:
1
2
3
4
5
6
7
8
while (row = fetch_row()) {
    records[row[0]][row[1]][row[2]]  = $row[3];
    records[row[0]][row[1]][ NULL ] += $row[3];
    records[row[0]][ NULL ][ NULL ] += $row[3];
    records[ NULL ][row[1]][row[2]] += $row[3];
    records[ NULL ][ NULL ][row[2]] += $row[3];
    records[ NULL ][row[1]][ NULL ] += $row[3];
}

[ Voor 11% gewijzigd door DJMaze op 05-01-2018 16:58 ]

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • Vircos
  • Registratie: Januari 2008
  • Laatst online: 06-10 09:22
Niet getest, maar kan zoiets niet:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
with cte as (
    select distinct id
    from (
        select id
        from table_a
        
        union all
        
        select id
        from table_b

        union all
        
        select id
        from table_c
    )
)
select
    a.id,
    b.id,
    c.id,
    count(*) aantal
from cte
    left outer join table_a a on
        cte.id = a.id
    left outer join table_b b on
        cte.id = b.id
    left outer join table_c c on
        cte.id = c.id
group by
    a.id,
    b.id,
    c.id


Eerst alle unieke ID's bepalen in een "moeder-tabel" m.b.v. common table expressie en dan alle combinatie via left-outer join bepalen.

het aantal bepalen verkeerd begrepen en aangepast

[ Voor 12% gewijzigd door Vircos op 05-01-2018 13:55 ]


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 14:02
Kan misschien iemand die zegt de vraag te begrijpen eens via dummy data duidelijk maken wat het doel is?

SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
create table table_a (
    id int
);

create table table_b (
    id int
);

create table table_c (
    id int
);

create table main_table (
    id int
);

create table other_table (
    main_table_id int,
    table_a_id int,
    table_b_id int,
    table_c_id int
);

insert into table_a values (NULL), (1), (2);
insert into table_b values (NULL), (1), (2);
insert into table_c values (NULL), (1), (2);


Wat staat er nu dan in main_table en other_table?

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
CurlyMo schreef op vrijdag 5 januari 2018 @ 13:55:
Kan misschien iemand die zegt de vraag te begrijpen eens via dummy data duidelijk maken wat het doel is?
egonolieux in "\[(My)SQL] Virtuele NULL waarde voor ID kolom in query"
Data:
ID AID BID CAantal
1115
1123


Tonen als:
ID AID BID CAantal
1115
1123
11null8 (5 + 3)

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 14:02
Ik was niet duidelijk genoeg. Ik snap het doel, maar niet wat er in main_table en other_table zou moeten staan om tot dat doel te komen.

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

Verwijderd

DJMaze schreef op vrijdag 5 januari 2018 @ 13:22:
@egonolieux Dus je bedoelt meer:
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
SELECT 
    table_a.id AS id_a,
    table_b.id AS id_b,
    table_c.id AS id_c,
    COUNT(*) AS item_count
FROM main_table
INNER JOIN other_table ON (other_table.main_table_id = main_table.id)
LEFT JOIN table_a ON (other_table.table_a_id = table_a.id)
LEFT JOIN table_b ON (other_table.table_b_id = table_b.id)
LEFT JOIN table_c ON (other_table.table_c_id = table_c.id)
GROUP BY 1, 2, 3

UNION SELECT 
    table_a.id AS id_a,
    table_b.id AS id_b,
    NULL AS id_c, /* virtueel */
    COUNT(*) AS item_count
FROM main_table
INNER JOIN other_table ON (other_table.main_table_id = main_table.id)
LEFT JOIN table_a ON (other_table.table_a_id = table_a.id)
LEFT JOIN table_b ON (other_table.table_b_id = table_b.id)
GROUP BY 1, 2, 3
Geen zin om het uit te proberen, maar is dat niet met IFNULL op te lossen? Laatste stukje:
SQL:
1
2
3
    LEFT JOIN table_a ON IFNULL(other_table.table_a_id, table_a.id) = table_a.id
    LEFT JOIN table_b ON IFNULL(other_table.table_b_id, table_b.id) = table_b.id
    LEFT JOIN table_c ON IFNULL(other_table.table_c_id, table_c.id) = table_c.id

Acties:
  • +1 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 09-09 13:58

NMe

Quia Ego Sic Dico.

Verwijderd schreef op vrijdag 5 januari 2018 @ 16:52:
[...]

Geen zin om het uit te proberen, maar is dat niet met IFNULL op te lossen? Laatste stukje:
SQL:
1
2
3
    LEFT JOIN table_a ON IFNULL(other_table.table_a_id, table_a.id) = table_a.id
    LEFT JOIN table_b ON IFNULL(other_table.table_b_id, table_b.id) = table_b.id
    LEFT JOIN table_c ON IFNULL(other_table.table_c_id, table_c.id) = table_c.id
Daarmee hou je nog steeds maar één record over per groep terwijl TS een record voor elke mogelijke permutatie wil.

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

  • egonolieux
  • Registratie: Mei 2009
  • Laatst online: 06-01-2024

egonolieux

Professionele prutser

Topicstarter
Heb het even met unions getest en dat geeft inderdaad het gewenste resultaat.

Maar ik denk niet dat dit een gangbare oplossing is; ik gebruik Doctrine ORM (PHP) voor mijn applicatie en die ondersteunt unions niet out of the box. Daarnaast doet de query met alle unions er meer dan een seconde over en voldoet dit niet aan mijn performance eisen.

Ik ben het eigenlijk eens dat dit binnen de applicatie zelf berekend hoort te worden, maar mijn vraag is hoe? De reden dat ik op SQL terugval, is omdat ik het naar mijn begrijpen als enig mogelijke oplossing zie.

Stel volgende data:

ID AID BID CAantalMax mogelijke aantal
11158100
1122590
1213380
122350
211725
21214100
221510
22215


Hoe kan je hieruit het aantal/max aantal berekenen voor bijvoorbeeld "1 null 2"? Ik kan naar mijn weten nergens uit afleiden of die 50 bij "1 2 2" tot dezelfde set data behoort als de 90 van "1 1 2".

Acties:
  • 0 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 09-09 13:58

NMe

Quia Ego Sic Dico.

egonolieux schreef op zaterdag 6 januari 2018 @ 17:29:
Hoe kan je hieruit het aantal/max aantal berekenen voor bijvoorbeeld "1 null 2"? Ik kan naar mijn weten nergens uit afleiden of die 50 bij "1 2 2" tot dezelfde set data behoort als de 90 van "1 1 2".
DJMaze in "\[(My)SQL] Virtuele NULL waarde voor ID kolom in query" heeft het letterlijk voorgedaan? Ik weet alleen niet of null als key van een array werkt, mogelijk zul je daar een magische waarde (0?) voor moeten gebruiken.

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

  • egonolieux
  • Registratie: Mei 2009
  • Laatst online: 06-01-2024

egonolieux

Professionele prutser

Topicstarter
NMe schreef op zaterdag 6 januari 2018 @ 17:37:
[...]

DJMaze in "\[(My)SQL] Virtuele NULL waarde voor ID kolom in query" heeft het letterlijk voorgedaan? Ik weet alleen niet of null als key van een array werkt, mogelijk zul je daar een magische waarde (0?) voor moeten gebruiken.
Nee dat gaat niet, het is niet gegarandeerd dat bijvoorbeeld die 50 en 90 samen 140 vormen. Het kan dat het geaggregeerde aantal van die 2 samen 100 is, en dat 40/50 40/90 de "items delen" en dat de resterende 10/50 50/90 unieke items zijn (stel het je voor als doorsnede).

Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 14:02
egonolieux schreef op zaterdag 6 januari 2018 @ 17:29:
Heb het even met unions getest en dat geeft inderdaad het gewenste resultaat.
Zou je eens kunnen aangeven wat er nu daadwerkelijk in de tabellen staat om tot een resultaat te komen? Ik ben blijkbaar de enige in dit topic, maar ik zie het niet.

Zou je dus de SQL uit mijn reactie kunnen aanvullen met een SQL om de tabellen te vullen?

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
Aangezien je PHP gebruikt kan je veel meer leuks doen dan dat m.b.v. een \Iterator.
Je knikkert je SQL result in een eigen Iterator class en zodra je fetch_row() op is laat je de next() method door de NULL resultaten van DJMaze in "\[(My)SQL] Virtuele NULL waarde voor ID kolom in query" loopen.

Oftewel:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class SQLResult implements \Iterator
{
    protected $null_rows, $current_row;

    public function current()
    {
        return $this->current_row;
    }

    public function next()
    {
        if ($row = fetch_row()) {
           $this->current_row = $row;
           // Doe iets met de row
           $this->null_rows["{$row[0]}-{$row[1]}-null"][3] += $row[3];
           $this->null_rows["{$row[0]}-null-null"][3] += $row[3];
        } else if ($this->null_rows) {
           $this->current_row = array_shift($this->null_rows);
        }
    }
}

$rows = new SQLResult();
foreach ($rows as $row) {
    // Doe iets met de row
}


Maar goed, je werkt jezelf in de nesten met ORM in PHP....
Zonder ORM is het sowieso sneller.

[ Voor 32% gewijzigd door DJMaze op 06-01-2018 18:28 . Reden: fix voorbeeldcode ]

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 09-09 13:58

NMe

Quia Ego Sic Dico.

egonolieux schreef op zaterdag 6 januari 2018 @ 18:05:
[...]

Nee dat gaat niet, het is niet gegarandeerd dat bijvoorbeeld die 50 en 90 samen 140 vormen. Het kan dat het geaggregeerde aantal van die 2 samen 100 is, en dat 40/50 40/90 de "items delen" en dat de resterende 10/50 50/90 unieke items zijn (stel het je voor als doorsnede).
Als het in code al niet op zo'n eenvoudige manier kan, hoe verwacht je dan dat het in SQL wél gaat lukken? Al verwacht ik dat je de code niet begrijpt ofzo, want dit doet hetzelfde als die union.

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
@NMe We weten toch dat elke ontwikkelaar altijd "Nee dat gaat niet" zegt ;)

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • egonolieux
  • Registratie: Mei 2009
  • Laatst online: 06-01-2024

egonolieux

Professionele prutser

Topicstarter
Ik had veel beter van bij het begin mijn werkelijke query ipv een vereenvoudigd voorbeeld gebruikt. Ik was even verward toen je zei dat het code voorbeeld hetzelfde doet als de union in @DJMaze zijn voorbeeld.

Hierbij echter niet:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
SELECT
    card_variant_type.id AS card_variant_type_id,
    card_language.id AS card_language_id,
    card_condition.id AS card_condition_id,
    (
        SELECT
            COUNT(DISTINCT card.id)
        FROM
            user_has_card_variant
            INNER JOIN card_variant ON user_has_card_variant.card_variant_id = card_variant.id
            INNER JOIN card ON card_variant.card_id = card.id
        WHERE
            user_has_card_variant.user_id = 216
            AND user_has_card_variant.card_condition_id = card_condition.id
            AND card_variant.type_id = card_variant_type.id
            AND card_variant.card_language_id = card_language.id
            AND card.expansion_id = expansion.id
            AND card.is_submission = 0
    ) AS card_amount_have,
    (
        SELECT
            COUNT(DISTINCT card.id)
        FROM
            card
            INNER JOIN card_variant ON card.id = card_variant.card_id
        WHERE
            card.expansion_id = expansion.id
            AND card.is_submission = 0
            AND card_variant.type_id = card_variant_type.id
            AND card_variant.card_language_id = card_language.id
    ) AS card_count
FROM
    expansion
    INNER JOIN card_variant_type ON (1 = 1)
    INNER JOIN card_language ON (1 = 1)
    INNER JOIN card_condition ON (1 = 1)
WHERE
    expansion.id = 124
ORDER BY
    card_variant_type_id ASC,
    card_language_id ASC,
    card_condition_id ASC


Met bijhorend schema: https://image.prntscr.com/image/J0M5N_iIQSCTtl36Oq5OSw.png (@CurlyMo vergeet mijn eerste voorbeeld)

Bij card_amount_have kan ik inderdaad de som berekenen, maar bij card_count gaat dit niet omwille van de reden die ik eerder vermeld heb (of vergis ik me?).

Bij een ORM heb je altijd slechtere performance per definitie. Maar hoe map jij je domain classes dan vanuit je tabellen? Manueel? ;)

[ Voor 4% gewijzigd door egonolieux op 06-01-2018 20:28 ]


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 14:02
@egonolieux Fijn om het schema te zien. Het zou nog handiger zijn als je ook direct je foreign key relaties zou hebben getekend en dummy data zou kunnen plaatsen (ik heb het al een paar keer gevraagd). Dan kunnen we tenminste met je mee valideren dat we exact het resultaat kunnen krijgen dat je zoekt. Ook SQL kan je zo prima unit testen

Om te beginnen met het schema:

Het gebruikers deel:
1. Je hebt gebruikers.
2. Die gebruikers zijn in bezit van een bepaald type kaarten.

Het kaarten deel:
1. Er zijn kaarten die wel of niet voorkomen in een uitbreiding.
2. Er zijn meerdere varianten kaarten waarvan er één de hoofdvariant is.
3. Kaarten kunnen in meerdere talen voorkomen.
4. Wat is submission?

Het kaarten - gebruikers relatie deel:
1. Wat zijn de conditions en hoe koppelt dit gebruikers aan kaarten?

Wat ik wel zie is dat het er op neer komt dat een gebruiker meerdere kaarten kan hebben met meerdere varianten met meerdere condities (weet niet wat dat is?). En dat een gebruiker van die combinatie ook nog dubbelen kan hebben wat opgeslagen staat in amount.

Wat ik dan niet begrijp is hoe bepaalde waarden in user_has_card_variant bepaalde waarden NULL kunnen zijn? Het heeft toch geen zin om daar een record in te hebben waarvan het user_id NULL is, want dat bestaat de relatie toch sowieso niet tussen een kaart en een gebruiker?

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • egonolieux
  • Registratie: Mei 2009
  • Laatst online: 06-01-2024

egonolieux

Professionele prutser

Topicstarter
Even om wat meer duidelijkheid te scheppen:

Je hebt inderdaad kaarten per uitbreiding. Elke kaart heeft minstens 1 variant die bestaat uit de combinatie van type en taal. Die is_main_type heeft eigenlijk weinig belang in deze context; die wordt bijna uitsluitend gebruikt voor het beperkt oplijsten van alle types in mijn applicatie. Een submission heeft ook weinig belang: gebruikers in mijn applicatie kunnen zelf kaarten toevoegen die ik achteraf moet goedkeuren. Ik wil hier dus enkel kaarten die reeds goedgekeurd zijn.

Een card condition is de staat waarin een kaart zich bevindt (beschadigd, goede staat,...); het is een applicatie voor het verzamelen van kaarten.

Een gebruiker heeft altijd varianten van een kaart in zijn verzameling, nooit kaarten zelf. De amount slaat inderdaad op dubbels. Als ik spreek over aantallen in mijn voorgaande posts gaat dit niet over deze amount, maar over het aantal unieke kaarten.

De waarden in user_has_card_variant kunnen inderdaad nooit NULL zijn. Wat ik met deze query doe is per combinatie van card_variant_type, card_language en card_condition het unieke aantal kaarten die de gebruiker in zijn verzameling heeft en het aantal unieke kaarten weergeven voor een specifieke expansie.

Zoals ik in mijn vorige posts reeds vermeld heb, zou ik naast dit overzicht ook geaggregeerde waarden willen hebben. Met "NULL" bedoel ik deze geaggregeerde waarden (er is bijvoorbeeld geen card_variant_type gespecificieerd).

Dit is een voorbeeldresultaat van de query:

card_variant_typecard_languagecard_conditioncard_amount_havecard_count
11158100
1122590
1213380
122350
211725
21214100
221510
22215


Het probleem is dat ik uit dit resultaat (naar mijn weten) niet kan afleiden hoeveel unieke kaarten een gebruiker heeft (card_amount_have) en hoeveel unieke kaarten er zijn (card_count) voor een specifieke expansion buiten SQL/in code.

Ik kan hier uiteraard een aparte query voor schrijven waarbij de card_variant_type, card_language en card_condition uit de WHERE clause laat. Een mogelijks resultaat is dan bijvoorbeeld 125. Maar ik wil niet alleen weten wat het unieke aantal is per expansie, ik wil elke mogelijke combinatie met een niet gespecifieerde card_variant_type, card_language en card_condition weten. Dit heb ik in mijn vorige posts met "NULL" uitgedrukt.

card_variant_typecard_languagecard_conditioncard_amount_havecard_count
nullnullnull111125


Voor elke mogelijke combinatie een aparte query schrijven is niet schaalbaar; vandaar dat ik dit in mijn huidige query wou integreren. Tot zo ver lijkt dit enkel te lukken met UNIONS waarbij ik de card_variant_type, card_language en card_condition selecteer als NULL en de WHERE clause voor deze tabellen in de subqueries herschrijf als een LEFT JOIN, maar dit is niet performant genoeg.

Ik denk niet dat het beter kan dan UNIONS, tenzij de query op een andere manier herschreven kan worden of iemand mij kan bewijzen dat je de geaggregeerde data in code uit het resultaat kan halen.

Wat dummy data betreft maakt het niet veel uit: de card_variant_type, card_language en card_condition tabellen hebben elk nooit meer dan 5-7 rijen in het echt. Per expansie zijn er gemiddeld 150 kaarten en per kaart alle combinaties van card_variant_type en card_language als card variant. Als je per se wilt kan ik je wel wat dummy data geven.

[ Voor 4% gewijzigd door egonolieux op 06-01-2018 22:35 ]


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 14:02
egonolieux schreef op zaterdag 6 januari 2018 @ 22:30:
De waarden in user_has_card_variant kunnen inderdaad nooit NULL zijn. Wat ik met deze query doe is per combinatie van card_variant_type, card_language en card_condition het unieke aantal kaarten die de gebruiker in zijn verzameling heeft en het aantal unieke kaarten weergeven voor een specifieke expansie.
Zoals je schema nu is opgebouwd weet je volgens mij niet welke card_language een specifieke gebruiker in zijn bezit heeft. Een gebruiker kan namelijk één card_variant hebben, waarbij die ene card_variant (unieke card_id en type_id) wel kan matchen met meerdere card_language_id (1, 1, 1) en (1, 1, 2). Je weet dan vervolgens niet welke card_language daadwerkelijk in bezit is van de gebruiker. Als ik op onderstaande dummy data dus de volgende query draai:
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
select
    sum(count),
    user_id
from
    (select
        count(*),
        user_id
    from
        user_has_card_variant as a
    inner join
        card_variant as b
    on
        a.card_variant_id = b.card_id
    group by
        user_id, b.language_id, card_condition_id
    ) as a
group by
    user_id;

Dan krijg je dit:
code:
1
2
4  1
2  2

Gebruiker 2 matched namelijk op beide card_languages.
Het probleem is dat ik uit dit resultaat (naar mijn weten) niet kan afleiden hoeveel unieke kaarten een gebruiker heeft (card_amount_have) en hoeveel unieke kaarten er zijn (card_count) voor een specifieke expansion buiten SQL/in code.
Dat komt wel goed denk ik, we moeten echter eerst naar je schema kijken of te zien of het daar wel goed gaat.
Ik denk niet dat het beter kan dan UNIONS, tenzij de query op een andere manier herschreven kan worden of iemand mij kan bewijzen dat je de geaggregeerde data in code uit het resultaat kan halen.

Wat dummy data betreft maakt het niet veel uit: de card_variant_type, card_language en card_condition tabellen hebben elk nooit meer dan 5-7 rijen in het echt. Per expansie zijn er gemiddeld 150 kaarten en per kaart alle combinaties van card_variant_type en card_language als card variant. Als je per se wilt kan ik je wel wat dummy data geven.
Ik kan me niet voorstellen dat het niet zou kunnen in SQL zonder unions. Daarbij moet ik wel opmerken dat ik op mijn werk voor 90% van de tijd in pure SQL rapporten maak op grote datasets, maar dan wel in PostgreSQL. Het maakt voor de mogelijke oplossing wel uit of het in een beetje fatsoenlijke database uitgevoerd kan worden. MySQL wordt dan misschien moeilijk.

Testdata zoals ik hem bij mijn tests heb opgezet (in PostgreSQL voor nu):
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
-- drop table expansion cascade;
-- drop table card cascade;
-- drop table card_variant cascade;
-- drop table card_language cascade;
-- drop table user_has_card_variant cascade;

create table expansion (
  id int primary key,
  name varchar(255)
 );

create table card (
   id int primary key,
   name varchar(255),
   expansion_id int references expansion(id) on delete cascade on update cascade
 );

create table card_language (
   id int primary key,
   name varchar(50),
   code char(2)
 );

create table card_variant (
   id int primary key,
   card_id int references card(id) on delete cascade on update cascade,
   type_id int,
   card_language_id int references card_language(id) on delete cascade on update cascade
);

create table user_has_card_variant (
  id int primary key,
  user_id int,
  card_variant_id int references card_variant(id) on delete cascade on update cascade,
  card_condition_id int,
  amount int
);

insert into card_language values (1, 'Nederlands', 'nl'), (2, 'Engels', 'en');
insert into expansion values (1, 'a'), (2, 'b');
insert into card values (1, 'a', 1), (2, 'b', 1), (3, 'c', 1), (4, 'd', 2), (5, 'e', 2);
insert into card_variant values (1, 1, 1, 1), (2, 1, 1, 2), (3, 2, 2, 1);
insert into user_has_card_variant values (1, 1, 1, 1, 1), (2, 1, 2, 1, 4), (3, 1, 2, 2, 3), (4, 1, 3, 3, 1), (5, 2, 1, 1, 5);


PS. zou je het schema nog eens kunnen plaatsen maar dan met de FK relaties. Dat leest veel makkelijker.

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
egonolieux schreef op zaterdag 6 januari 2018 @ 20:18:
Bij een ORM heb je altijd slechtere performance per definitie. Maar hoe map jij je domain classes dan vanuit je tabellen? Manueel? ;)
Ja en nee, hangt van de situatie af.
Sowieso heb ik geen velden als "id" en "name" want dat compliceert de boel.
Jouw ORM doet dat wel en daardoor kan ik geen fancy pancy queries schrijven.

Mijn database is gewoon:
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CREATE TABLE card_languages (
    card_language_id SERIAL,
    card_language_name VARCHAR(50),
    card_language_code VARCHAR(5) /* 'nl' | 'nl-NL' | 'nl-BE' */
);

CREATE TABLE cards (
    card_id SERIAL,
    card_name VARCHAR(100),
    card_is_submission TINYINT
);

CREATE TABLE cards_variants (
    card_variant_id SERIAL,
    card_id INT,
    card_language_id INT
);

Waarom? Nou gewoon:
SQL:
1
2
3
4
5
6
7
8
9
10
11
SELECT
    card_id,
    card_name,
    card_is_submission,
    card_variant_id,
    card_language_id,
    card_language_name,
    card_language_code
FROM cards
INNER JOIN cards_variants USING (card_id)
INNER JOIN card_languages USING (card_language_id)


En wat doet jouw ORM:
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
SELECT
    card.id,
    card.name,
    card.is_submission,
    cards_variants.id,
    cards_variants.card_id,
    cards_variants.card_language_id,
    card_language.id,
    card_language.name,
    card_language.code
FROM cards
INNER JOIN cards_variants ON (cards_variants.card_id = card.id)
INNER JOIN card_languages ON (card_languages.id = cards_variants.card_language_id)

Snap je het 'id' en 'name' probleem?

[ Voor 21% gewijzigd door DJMaze op 07-01-2018 15:36 . Reden: Voorbeeld duidelijker gemaakt ]

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • egonolieux
  • Registratie: Mei 2009
  • Laatst online: 06-01-2024

egonolieux

Professionele prutser

Topicstarter
@CurlyMo Hier is mijn schema met relaties getekend: https://image.prntscr.com/image/-5F4nfGbRiSTwxCPXzRXXg.png.

Waarom zou je de card languages per gebruiker niet kunnen weergeven? Met deze query lukt dat zonder problemen:

code:
1
2
3
4
5
6
7
8
9
10
11
12
SELECT
    user_has_card_variant.user_id AS user_id,
    card_variant.card_language_id AS card_language_id,
    COUNT(DISTINCT card.id) AS card_count
FROM
    card
    INNER JOIN card_variant ON card.id = card_variant.card_id
    INNER JOIN user_has_card_variant ON card_variant.id = user_has_card_variant.card_variant_id
WHERE
    user_has_card_variant.user_id = 216
    AND card_variant.card_language_id = 1
    AND card.expansion_id = 124


Misschien begrijp ik je verkeerd, maar de query die je gaf lijkt me geen werkende query?

@DJMaze Klopt, maar ik schrijf mijn queries meestal zo vendor onafhankelijk mogelijk (ik denk dat USING enkel in MySQL werkt)? Dus het specifieke ID probleem dat je hier vermeld heb ik nog nooit gehad.

Je kan zo waarschijnlijk nog tal van voorbeelden geven van zaken die in een ORM niet elegant werken tov native SQL, maar als je met een groot aantal domain classes zit, zie ik niet echt een beter alternatief dan een ORM. De voordelen lijken mij groter dan de nadelen.

Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 14:02
egonolieux schreef op zondag 7 januari 2018 @ 15:36:
@CurlyMo Hier is mijn schema met relaties getekend: https://image.prntscr.com/image/-5F4nfGbRiSTwxCPXzRXXg.png.

Waarom zou je de card languages per gebruiker niet kunnen weergeven? Met deze query lukt dat zonder problemen:

code:
1
2
3
4
5
6
7
8
9
10
11
12
SELECT
    user_has_card_variant.user_id AS user_id,
    card_variant.card_language_id AS card_language_id,
    COUNT(DISTINCT card.id) AS card_count
FROM
    card
    INNER JOIN card_variant ON card.id = card_variant.card_id
    INNER JOIN user_has_card_variant ON card_variant.id = user_has_card_variant.card_variant_id
WHERE
    user_has_card_variant.user_id = 216
    AND card_variant.card_language_id = 1
    AND card.expansion_id = 124


Misschien begrijp ik je verkeerd, maar de query die je gaf lijkt me geen werkende query?
Als je zelf de card_language_id als parameter van je conditie wilt opgeven natuurlijk wel :) Ik dacht vanuit het idee om alle aggregaties van alle gebruikers te krijgen zonder aanvullende condities.

Kan je aangegeven of mijn dummy data is wat je bedoelt?

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
@egonolieux Ja ik begrijp waar je heen wilt.
En daarom heb ik, zeg maar:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
namespace Cards;
class Languages extends \DB\Table
{
    function __construct()
    {
        $this->table = strtolower(str_replace('\\', '_', static::class)); // cards_languages
    }
}

class Language extends \DB\TableRecord
{
    function __construct(\DB\Table $table)
    {
        $this->field_prefix = strtolower(str_replace('\\', '_', static::class)).'_'; // cards_language
    }
}

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • egonolieux
  • Registratie: Mei 2009
  • Laatst online: 06-01-2024

egonolieux

Professionele prutser

Topicstarter
@CurlyMo Ah, ik denk dat ik snap wat je bedoel. Dat klopt inderdaad, maar het gaat altijd over 1 specifieke gebruiker. Ik wil namelijk een overzicht wat een specifieke gebruiker allemaal heeft in een specifieke expansie. (zie de user_has_card_variant.user_id = 216 in de subquery).

Je dummy data ziet er goed uit.

[ Voor 5% gewijzigd door egonolieux op 07-01-2018 15:59 ]


Acties:
  • +2 Henk 'm!

  • KabouterSuper
  • Registratie: September 2005
  • Niet online
Waar de TS volgens mij simpelweg op zoek is, is een rollup. Rollups (en cubes) worden niet standaard ondersteund door elke database, hoewel het tegenwoordig wel onderdeel van de ANSI standaard is. In MSSQL en Oracle is het in elk geval wel standaard sinds de steentijd. Ook mysql heeft tegenwoordig rollups, al is de functionaliteit nogal basaal (zie https://dev.mysql.com/doc/refman/5.7/en/group-by-modifiers.html).
Overigens, het mooie van rollups is dat de extra aggregaties qua performance vrijwel gratis zijn (de data zit toch al in het snelste stuk van het geheugen), en de query simpel en logisch blijft.Dus in dit geval zou ik niet terugvallen op extra code buiten de query...

When life gives you lemons, start a battery factory


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 14:02
egonolieux schreef op zondag 7 januari 2018 @ 15:58:
@CurlyMo Ah, ik denk dat ik snap wat je bedoel. Dat klopt inderdaad, maar het gaat altijd over 1 specifieke gebruiker. Ik wil namelijk een overzicht wat een specifieke gebruiker allemaal heeft in een specifieke expansie. (zie de user_has_card_variant.user_id = 216 in de subquery).

Je dummy data ziet er goed uit.
Kan je nu een tabelletje laten zien op basis van die dummy data waarmee je aangeeft wat precies het eindresultaat moet zijn?

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • egonolieux
  • Registratie: Mei 2009
  • Laatst online: 06-01-2024

egonolieux

Professionele prutser

Topicstarter
@KabouterSuper Van rollups had ik nog niet gehoord en het ziet er op het eerste zicht belovend uit. Heb even een test gedaan met volgende query:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT
    card_variant_type.id AS card_variant_type_id,
    card_language.id AS card_language_id,
    card_condition.id AS card_condition_id
FROM
    card_variant_type
    CROSS JOIN card_language
    CROSS JOIN card_condition
WHERE
    card_variant_type.id IN (1, 2, 3)
    AND card_condition.id IN (1, 2, 3)
    AND card_language.id IN (1, 2, 3)
GROUP BY
    card_variant_type.id ASC,
    card_language.id ASC,
    card_condition.id ASC
    WITH ROLLUP


Dit geeft het volgende resultaat:

111
112
113
11NULL
121
122
123
12NULL
131
132
133
13NULL
1NULLNULL
211
212
213
21NULL
221
222
223
22NULL
231
232
233
23NULL
2NULLNULL
311
312
313
31NULL
321
322
323
32NULL
331
332
333
33NULL
3NULLNULL
NULLNULLNULL


Zoals je ziet is dit niet helemaal het gewenste resultaat omdat de GROUP BY na de CROSS JOINs uitgevoerd wordt. Is het misschien toch mogelijk alle mogelijke combinaties met NULL te krijgen ((3 + 1)^3 = 64 rijen)?

@CurlyMo Blijkbaar niet goed gekeken, maar je bent de type_id kolom vergeten bij card_variant. Ik zal het even zelf verder aanvullen en 2 card variant types met ID 1 en 2 gebruiken:

code:
1
2
3
4
insert into card_variant values
(1, 1, 1, 1),
(2, 1, 1, 2),
(3, 2, 2, 1);


Dan zou dat volgende gewenste data moeten geven voor expansion 1 en gebruiker 1 volgens jouw dummy data:

card_variant_type_idcard_language_idcard_condition_idcard_amount_havecard_count
NULLNULLNULL22
NULLNULL112
NULLNULL212
NULLNULL312
NULL1NULL22
NULL1112
NULL1202
NULL1312
NULL2NULL11
NULL2111
NULL2211
NULL2301
1NULLNULL11
1NULL111
1NULL211
1NULL301
11NULL11
11111
11201
11301
12NULL11
12111
12211
12301
2NULLNULL11
2NULL101
2NULL201
2NULL311
21NULL11
21101
21201
21311
22NULL00
22100
22200
22300

[ Voor 19% gewijzigd door egonolieux op 07-01-2018 19:38 ]


Acties:
  • 0 Henk 'm!

  • KabouterSuper
  • Registratie: September 2005
  • Niet online
egonolieux schreef op zondag 7 januari 2018 @ 19:35:
@KabouterSuper Van rollups had ik nog niet gehoord en het ziet er op het eerste zicht belovend uit. Heb even een test gedaan met volgende query:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT
    card_variant_type.id AS card_variant_type_id,
    card_language.id AS card_language_id,
    card_condition.id AS card_condition_id
FROM
    card_variant_type
    CROSS JOIN card_language
    CROSS JOIN card_condition
WHERE
    card_variant_type.id IN (1, 2, 3)
    AND card_condition.id IN (1, 2, 3)
    AND card_language.id IN (1, 2, 3)
GROUP BY
    card_variant_type.id ASC,
    card_language.id ASC,
    card_condition.id ASC
    WITH ROLLUP


Dit geeft het volgende resultaat:

111
112
113
11NULL
121
122
123
12NULL
131
132
133
13NULL
1NULLNULL
211
212
213
21NULL
221
222
223
22NULL
231
232
233
23NULL
2NULLNULL
311
312
313
31NULL
321
322
323
32NULL
331
332
333
33NULL
3NULLNULL
NULLNULLNULL


Zoals je ziet is dit niet helemaal het gewenste resultaat omdat de GROUP BY na de CROSS JOINs uitgevoerd wordt. Is het misschien toch mogelijk alle mogelijke combinaties met NULL te krijgen ((3 + 1)^3 = 64 rijen)?
Wat je wil zijn grouping sets en/of cube operators; die doen precies wat je wil. En laten die nou net niet ondersteund worden door mysql.

When life gives you lemons, start a battery factory


Acties:
  • Beste antwoord
  • +1 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 14:02
@egonolieux Ik zou toch kijken naar wat @Vircos al voorstelde. Dus een moedertabel maken die de basis legt voor je joins incl NULLs. Onderstaande voorbeeld gaat uit van generate_series in PostgreSQL, maar generate_series is ook na te bouwen in MySQL las ik.

SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
with cte as (
    select
        NULL::int as card_variant_type,
        NULL::int as card_language_id,
        NULL::int as card_condition_id
    union all
    select
        a.*,
        b.*,
        c.*
    from 
        (select
            generate_series (
                (select min(id) from card_variant),
                (select max(id) from card_variant)
            )
        union all
            select NULL
        ) as a,
        (select
            *
        from
            generate_series (
                (select min(id) from card_language),
                (select max(id) from card_language)
            )
        union all
            select NULL
        ) as b,
        (select
            *
        from
            generate_series (
                (select min(card_condition_id) from user_has_card_variant),
                (select max(card_condition_id) from user_has_card_variant)
            )
        union all
            select NULL
        ) as c
)
select
    *
from
    cte
union
select
    *
from
    cte
order by 1, 2, 3

De laatste union is bedoeld voor een laatste ontdubbeling

card_variant_typecard_language_idcard_condition_id
111
112
113
11
121
122
123
12
11
12
13
1
211
212
213
21
221
222
223
22
21
22
23
2
311
312
313
31
321
322
323
32
31
32
33
3
11
12
13
1
21
22
23
2
1
2
3
  

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • egonolieux
  • Registratie: Mei 2009
  • Laatst online: 06-01-2024

egonolieux

Professionele prutser

Topicstarter
Misschien een wat late reactie, maar omdat ik al redelijk snel door had dat dit niet te vertalen was naar mijn ORM (common table expressions en unions), heb ik ondertussen mijn applicatie aangepast zodat deze query niet echt meer nodig is.

Zelfs moest ik de query native uitvoeren, blijft het een heel geklus met MySQL.
Misschien dat ik bij mijn eerst volgende project eens voor Postgres ga kiezen als ik van plan ben dergelijke queries te schrijven ;). Eigenlijk moet ik dan deze query zelfs niet schijven aangezien rollup, cube en grouping sets ondersteund worden.

[ Voor 11% gewijzigd door egonolieux op 19-02-2018 20:12 ]

Pagina: 1