Jump to content
php.lv forumi

MySQL dīvainības ar mainīgajiem


marrtins

Recommended Posts

Dots pieprasījums (vienkāršots)

SELECT SQL_CALC_FOUND_ROWS 
@vendor_id:=(<SELECT bla bla LIMIT 1>) AS vendor_id, // INTEGER vai NULL
@has_vendor:=(<SELECT bla bla LIMIT 1>) AS has_vendor, // INTEGER vai NULL
@has_vendor_id:=COALESCE(@vendor_id, @has_vendor) AS has_vendor_id
FROM
_preces

 

Jebkāda filtrēšana pēc has_vendor_id vai @has_vendor_id NEDARBOJAS.

WHERE
specid =1191 AND (@has_vendor_id = 2)
----
WHERE
specid =1191
HAVING
has_vendor_id = 2

 

Savukārt, pēc katra individuāli DARBOJAS

WHERE
specid =1191 AND (@vendor_id = 2)
---
WHERE
specid =1191 AND (@has_vendor = 2)
---
WHERE
specid =1191
HAVING
vendor_id = 2

 

Selektā bez nosacījumiem skaidri redzams, ka has_vendor_id aizpildās kā vajadzīgs kā būtu jābūt izmantojot COALESCE. Aizdomas, ka tur kaut-kur pa vidu veidojas BLOB, taču nekādi CAST vai CONVERT arī nelīdz.

 

Subkveriji šajā gadījumā ir nepieciešami, joinus nepiedāvāt.

 

Idejas? :O

Edited by marrtins
Link to comment
Share on other sites

Ja godīgi, maz ko zinu par MySQL mainīgajiem, bet vai šai gadījumā nav tā, ka vispirms tiek izpildīta where klauza un tad tikai iniciēts mainīgais @has_vendor_id. Līdz ar to ir vismaz 2 iespējas, manuprāt:

1) rakstīt nosacījumu uz individuālajiem mainīgajiem, jo coalesce jau ir tikai f-ja un to var gana viegli nosimulēt papildus where nosacījumos ar mazliet garākiem AND/OR nosacījumiem

2) rakstīt virsselektu, kuram subselekts ir šis te jau dotais (iespējams bez SQL_CALC_FOUND_ROWS, kuru var iznest virsselektā) un where klauzu pielietot virsselektā, kur @has_vendor_id jau būtu jābūt izrēķinātam.

 

Gints Plivna

http://datubazes.wordpress.com

Link to comment
Share on other sites

Ja godīgi, maz ko zinu par MySQL mainīgajiem, bet vai šai gadījumā nav tā, ka vispirms tiek izpildīta where klauza un tad tikai iniciēts mainīgais @has_vendor_id.

Diezvai, jo plain selektā viss rādās "kā vajag", kā arī, selektējot pēc abiem pirmajiem mainīgajiem, arī viss darbojas.

 

Līdz ar to ir vismaz 2 iespējas, manuprāt:

1) rakstīt nosacījumu uz individuālajiem mainīgajiem, jo coalesce jau ir tikai f-ja un to var gana viegli nosimulēt papildus where nosacījumos ar mazliet garākiem AND/OR nosacījumiem

Jā, patlaban esmu izlīdzējies ar šo variantu, bet baisi negribas šo lieko drazu katrā vietā, kur tiek uzstādīti filtri. Protams, var jau ieviest kādu f-iju. Lai nu kā, bet rezultāts ir visai interesants, gribētos zināt kāpēc tā.

 

2) rakstīt virsselektu, kuram subselekts ir šis te jau dotais (iespējams bez SQL_CALC_FOUND_ROWS, kuru var iznest virsselektā) un where klauzu pielietot virsselektā, kur @has_vendor_id jau būtu jābūt izrēķinātam.

 

Šis nebūs risinājums jo 1) performance aizies dupsītī 2) pieprasījums jau tā ir visai liels, papildu un papildus sarežģītības slāni negribas ieviest.

 

Paldies par ātru atbildi!

Edited by marrtins
Link to comment
Share on other sites

Tur BTW ir kaut kādas acīmredzamas dīvainības kad tie mianīgie tiek un netiek inicializēti. Skat uzskatāms testpiemērs. Man ir tabula t1 kurā ir kolona a, tabulā t1 ir ieraksts ar vērtību 2, bet nav ieraksta ar vērtību 77.

Šis testpiemērs īsti nestrādā, kā domāts (vismaz nezinot mainīgo inicializācijas smalkumus), jo viņam vajadzētu atgriezt ierakstu:

mysql> select @i := (select a from t1 where a = 77) as k1,
   ->        @j := (select a from t1 where a = 2) as k2,
   ->        @e := coalesce(@i, @j) as k3
   -> from dual
   -> where @e = 2;
Empty set (0.00 sec)

OK tagad izpildam šo pašu selektu vienreiz bez where klauzas:

mysql> select @i := (select a from t1 where a = 77) as k1,
   ->        @j := (select a from t1 where a = 2) as k2,
   ->        @e := coalesce(@i, @j) as k3
   -> from dual;
+------+------+------+
| k1   | k2   | k3   |
+------+------+------+
| NULL |    2 |    2 |
+------+------+------+
1 row in set (0.00 sec)

UN TAGAD lai dzīve kā medus nelikto izpildam precīzi to pašu pirmo SQL teikumu:

mysql> select @i := (select a from t1 where a = 77) as k1,
   ->        @j := (select a from t1 where a = 2) as k2,
   ->        @e := coalesce(@i, @j) as k3
   -> from dual
   -> where @e = 2;
+------+------+------+
| k1   | k2   | k3   |
+------+------+------+
| NULL |    2 |    2 |
+------+------+------+
1 row in set (0.00 sec)

 

Bingo! šoreiz ir rezultāts atgriezts! Ļoti jau nu ož pēc tā ka pirmā SQL teikumā tomēr @e netika izrēķināts, tb vispirms bija where klauza, kas visu jau nogrieza nost. Tad otrā SQL teikumā @e tika izrēķināts, jo where klauzas vispār nebija. Tad trešajā SQL teikumā jau izmantoja to pašu @e, kas jau bija izrēķināts otrā SQL teikumā.

 

Un galu galā jālasa MySQL dokumentācija, jo tur jau patiesība viss ir rakstīts:

As a general rule, you should never assign a value to a user variable and read the value within the same statement. You might get the results you expect, but this is not guaranteed. The order of evaluation for expressions involving user variables is undefined and may change based on the elements contained within a given statement. In SELECT @a, @a:=@a+1, ..., you might think that MySQL will evaluate @a first and then do an assignment second. However, changing the statement (for example, by adding a GROUP BY, HAVING, or ORDER BY clause) may cause MySQL to select an execution plan with a different order of evaluation.

 

Gints Plivna

http://datubazes.wordpress.com

Link to comment
Share on other sites

Nē nu bāc, tas MySQL man sāk tracināt uz šīm niansēm. Praktiski šāda variabļu "fīča" SELECT pieprasījumā sanāk bezjēdzīga. Būs viss jāpārraksta. It kā jau loģiski, ka tā inicializēšanās secība nav definēta, ja darbojas ar kopām, bet nu tad nafig tādi vispār vajadzīgi SELECTā? :E

 

Atminos, vēl pirms laika, kad šāda tipa nesaprotami MySQL gļuki parādījās pie datubāzes ar procedūrām dump un restore uz citu serveri, kad mysqldump pie procedūras definīcijas piekabināja DEFINER = 'user@host'. Gala serverī tāds users neeksistē, viss saimportējas, bet procedūra nedarbojas un nekādi skaļi paziņojumi par to netiek doti. Kamēr to gļuku atradu... :E

 

Paldies \m/

Edited by marrtins
Link to comment
Share on other sites

Nu kā jau dokā rakstīts vismaz sākotnējais šo mainīgo mērķis ir bijis šāds:

You can store a value in a user-defined variable in one statement and then refer to it later in another statement. This enables you to pass values from one statement to another.

Bet tad šo fīču izdomas pilni cilvēki izmanto visādām vajadzībām, kuras arī tai pašā lapā zemāk var redzēt un šķiet vairums no tām mainīgos lieto viena selekta ietvaros. Kas jau laikam tomēr vairuma gadījumā arī strādā. Bet iespējas nepareiza lietošana (abuse) jau nav tikai šai konkrētajai MySQL fīčai, tā ir daudz kur :)

 

Bet nu īstenībā šai gadījumā visās citās DB (ok vismaz Oraclē un SQL Serverī), kur tādu mainīgo nav, es lietotu iekļauto selektu ar where klauzu virsselektā. It kā jau MySQLā tā mantra ir, ka apakšvaicājumi ir bremzīgi, bet man ir aizdomas, ka tas itin bieži ir mīts, tāpat kā daudzi citi mīti citās DBVS. Katrā ziņā vismaz ir vērts pamēģināt.

 

Ak jā un vēl vajag atcerēties, ka mainīgie ir konekcijas ietvaros, tātad, ja vienas konekcijas ietvaros to pašu izpilda atkal, jāuzmanās, lai iepriekšējos SQL teikumos inicializētās vērtības nesāk jaukt gaisu...

 

Gints Plivna

http://datubazes.wordpress.com

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...