WN

WN (https://www.wn.se/forum/index.php)
-   Serversidans teknologier (https://www.wn.se/forum/forumdisplay.php?f=4)
-   -   beror detta fel på gammal MySQL-version? (https://www.wn.se/forum/showthread.php?t=9328)

koala 2005-08-12 12:36

Utvecklar grejer till en kund som har webbhotell hos cliche. Har använt version 4.x när jag utvecklat koden, men upptäckte att cliche har version 3. Misstänker att versionsskillnaden är boven i dramat, eller är jag ute och cyklar här? Som sagt använder jag själv 4.x och får inte något felmeddelande av följande query (ej heller på scorpionshops server), men på cliche blir det fel:

Kod:

You have an error in your SQL syntax near '(SELECT p.category_id cId,
          COUNT(p.product_id) as category_product_count
 ' at line 4
Whole query:
        SELECT c.*, t.category_product_count
          FROM shop_category c
          LEFT JOIN
          (SELECT p.category_id cId,
          COUNT(p.product_id) as category_product_count
          FROM shop_product p
          GROUP BY p.category_id
          ) t
          ON
          t.cId = c.category_id

Databasen är rätt så identisk på cliches server och min, bortsett från att jag var tvungen att modifiera lite grejer som tydligen inte stöds i MySQL 3.x såsom att jag ändrat tabellerna från:
Kod:

CREATE TABLE `shop_product` (
 `product_id` int(11) unsigned NOT NULL auto_increment,
 `category_id` int(11) NOT NULL default '0',
 `product_price` decimal(15,4) NOT NULL default '0.0000',
 `product_is_call_for_price` tinyint(1) NOT NULL default '0',
 `product_is_always_free_shipping` tinyint(1) NOT NULL default '0',
 `product_date_added` datetime NOT NULL default '0000-00-00 00:00:00',
 `product_date_modified` datetime default NULL,
 `product_date_available` datetime default NULL,
 `product_image` tinytext,
 `product_quantity` int(11) NOT NULL default '0',
 `product_custom_id` varchar(32) default NULL,
 PRIMARY KEY (`product_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

till:

Kod:

CREATE TABLE `shop_product` (
 `product_id` int(11) unsigned NOT NULL auto_increment,
 `category_id` int(11) NOT NULL default '0',
 `product_price` decimal(15,4) NOT NULL default '0.0000',
 `product_is_call_for_price` tinyint(1) NOT NULL default '0',
 `product_is_always_free_shipping` tinyint(1) NOT NULL default '0',
 `product_date_added` datetime NOT NULL default '0000-00-00 00:00:00',
 `product_date_modified` datetime default NULL,
 `product_date_available` datetime default NULL,
 `product_image` tinytext,
 `product_quantity` int(11) NOT NULL default '0',
 `product_custom_id` varchar(32) default NULL,
 PRIMARY KEY (`product_id`)
) TYPE=MyISAM;

Tycker inte att denna ändring bör påverka...

Så här ser shop_category ut:

Kod:

CREATE TABLE `shop_category` (
 `category_id` int(11) unsigned NOT NULL auto_increment,
 `category_parent_id` int(11) NOT NULL default '0',
 `category_sort_order` int(3) default NULL,
 `category_image` varchar(128) default '',
 `category_date_added` datetime default NULL,
 `category_date_modified` datetime default NULL,
 PRIMARY KEY (`category_id`),
 KEY `NewIndex` (`category_parent_id`,`category_id`)
) TYPE=MyISAM;


Mycket(!) tacksam för information om vad felet beror på!





Lite kul kan man väl ha åt följande mail? Varför ens svara när man inte svarar på kundens frågor? (Ok jag vet att man får vad man betalar för, och jag ska försöka få kunden att byta webbhotell...)
Citat:

Hej X

Det är inte något jag vet av, men det kan vära att det kommer engång
till näste år.

Vänliga hälsningar
X X

-------------------------------
Cliche Webhosting Sweden
X
Tlf. X
Fax. X
-------------------------------

x wrote:
> Hej, jag ser att MySQL-versionen som används på mitt konto är: MySQL 3.23.56
>
> Jag skulle behöva version 4.x för en vidareutveckling. Är det möjligt att ordna detta på något sätt? Eller blir vi tvungna att flytta till ett annat webbhotell?
>
> MVH X


eg0master 2005-08-12 12:42

Citat:

beror detta fel på gammal MySQL-version?
med största sannolikhet ja.
Subquerys kom ganska sent i mysql (v4.1 som det verkar).

http://dev.mysql.com/doc/mysql/en/rewritin...subqueries.html

koala 2005-08-12 13:21

Som jag misstänkte då med andra ord... Vill inte ändra min kod för att bli bakåtkompatibel egentligen. Men är det kanske dumt att använda subqueries överhuvudtaget? (Eftersom det nu kom så sent i MySQL...)

eg0master 2005-08-12 13:41

Om man använder mysql är subquerys dumt (eftersom det bara stöds i de senaste versionerna och mysqlinstallationer har en tendens att inte vara helt up-to-date), men annars är det ju ett utmärkt sätt att låta databasen göra jobbet istf att göra det själv i kod.

Dock är väl din fråga knappast något som verkligen kräver en subquery... Det gör den bara onödigt komplicerad.
Kod:

SELECT c.category_id, c.category_name, count(*)
FROM shop_category c, shop_product p
WHERE c.category_id = p.category_id
GROUP BY c.category_id, c.category_name

Det är ju bara listan i group by som måste innehålla alla kolumner från shop_category du skall plocka ut (och sluta göra select * - det är en dålig ovana - ange alltid vad du vill ha).
Min query blir dessutom sannolikt snabbare eftersom den slipper göra en subquery.

Starweb 2005-08-12 18:10

Citat:

Originally posted by eg0master@Aug 12 2005, 12:41
(och sluta göra select * - det är en dålig ovana - ange alltid vad du vill ha).
Min query blir dessutom sannolikt snabbare eftersom den slipper göra en subquery.

Gör ej heller count(*), det påverkar också negativt. Skriv då exakt vad du vill count:a. Att föredra är såfall databasens id, tex count(c.category_id), om det nu är det du ska beräkna...

grazzy 2005-08-12 21:12

OT:
Kod:

#!/usr/local/bin/perl -w

use DBI;

require ("database_login.pl");
my $blah = subnet_connect();
my $dbh = $$blah;
######

my ($sql,$sth,$sql2,$sth2,$t1,$t2,$t3,$t4,$iters);
$iters = 100000;
$sql = 'SELECT SQL_NO_CACHE COUNT(*) FROM article';
$sth = $dbh->prepare($sql);

$t1 = time();
for($i=0;$i<$iters;$i++) {
    $sth->execute();
}
$t2 = time();
$sth->finish();

$sql2 = 'SELECT SQL_NO_CACHE COUNT(id) FROM article';
$sth = $dbh->prepare($sql2);
$t3 = time();
for($i=0;$i<$iters;$i++) {
    $sth->execute();
}
$t4 = time();
$sth->finish();
print "COUNT(*) " . ($t2-$t1) . " seconds\n";
print "COUNT(id) " . ($t4-$t3) . " seconds\n";
$dbh->disconnect();

Tabellen har ca 1 miljon rader och har ett par rätt stora fält.

Körningar (3):
Kod:

COUNT(*) 10.6 seconds
COUNT(id) 11 seconds

Testade även att selecta * mot att selecta alla fält. Här körde jag en LIMIT 100 iom att bandbredden blir en flaskhals annars. 1000 iterationer. 4 körningar
Kod:

* 5 seconds
fält 5.8 seconds

Vid en första anblick ser detta försumbart ut, dock blir siffrorna annorlunda när man tittar närmare på dem.

100,000 frågor på en tabell med 1,000,000 rader ger en diff på ungefär 3,6% (om jag kommer ihåg min lågstadiematte rätt).

I fallet med SELECT är skillnaden större, nämligen 13,8% med 1000 iterationer över 100 rader (den här växer då rätt snabbt till en betydande skillnad).

koala 2005-08-12 23:17

Citat:

Originally posted by eg0master@Aug 12 2005, 13:41
Om man använder mysql är subquerys dumt (eftersom det bara stöds i de senaste versionerna och mysqlinstallationer har en tendens att inte vara helt up-to-date), men annars är det ju ett utmärkt sätt att låta databasen göra jobbet istf att göra det själv i kod.
Dock är väl din fråga knappast något som verkligen kräver en subquery... Det gör den bara onödigt komplicerad.
Kod:

SELECT c.category_id, c.category_name, count*
FROM shop_category c, shop_product p
WHERE c.category_id = p.category_id
GROUP BY c.category_id, c.category_name

Det är ju bara listan i group by som måste innehålla alla kolumner från shop_category du skall plocka ut (och sluta göra select * - det är en dålig ovana - ange alltid vad du vill ha).
Min query blir dessutom sannolikt snabbare eftersom den slipper göra en subquery.


Det där tipset är jag mycket tacksam för, och ska byta ut min query. Nästan så man skäms ju ;)

Jag kommer inte att lyssna på ditt tips om att specifikt ange fält, utan kör på *. Visserligen håller jag med dig om att det i teorin är bäst att ange sina fält. Dock inte i praktiken :) (Jag vet vad alla fält heter ändå. Det går även att justera detta i efterhand om man känner för det, men för att spara tid (och även få snabbare querys -- källa: grazzy) kör jag * just nu...)

EDIT: Det finns ett litet problem när jag byter till din query: De kategorier som är tomma (dvs det saknas produkter i dem) kommer inte med nu, vilket jag vill att de ska göra, och få en product_count på 0 eller NULL... Ska fundera på detta efter "frukost"...

grazzy 2005-08-13 01:24

Det brukar man lösa med LEFT JOINS vs INNER JOINS.. Du använder nu en innerjoin, det innebär att raden bara visas om det finns ett fält i båda tabellerna du joinar som stämmer. En left tar alltid med om det finns något i den vänstra.

Hmm, jag inser just att mina benchmarks visar resulta tvärtom mot all vedertagen kunskap. Kan någon vara snäll att peka ut vad jag har gjort för fel, eller uppvisa resultat som påvisar annorlunda? :)

koala 2005-08-13 02:57

Citat:

Originally posted by grazzy@Aug 13 2005, 01:24
Det brukar man lösa med LEFT JOINS vs INNER JOINS.. Du använder nu en innerjoin, det innebär att raden bara visas om det finns ett fält i båda tabellerna du joinar som stämmer. En left tar alltid med om det finns något i den vänstra.
Hmm, jag inser just att mina benchmarks visar resulta tvärtom mot all vedertagen kunskap. Kan någon vara snäll att peka ut vad jag har gjort för fel, eller uppvisa resultat som påvisar annorlunda?


Ja just ja, jag tänkte inte på att det faktiskt är en JOIN eftersom det inte står JOIN någonstans i queryn ;) Så här skrev jag nu:
Kod:

SELECT c.*, count(p.product_id) category_product_count
FROM shop_category c
LEFT JOIN shop_product p
ON c.category_id = p.category_id
GROUP BY c.category_id

och det bör fungera bra... (har inte provat ännu dock, men räknar med nemas problemas).


Alla tider är GMT +2. Klockan är nu 21:42.

Programvara från: vBulletin® Version 3.8.2
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Svensk översättning av: Anders Pettersson