on 1/2/04 12:59 PM, Adam i Agnieszka Gasiorowski FNORD wrote:

Thanks for the info. Just so I'm sure I know what I should: :)

1) You only want the day's articles. These are records in the article table and 
nowhere else.
2) An article will always belong to at least one section, and may belong to multiple 
sections. In either case, it should always be counted once.
3) There are several bit flags used for specific reasons that must be considered. (You 
may want to consider changing these flag fields to a SET type for better readability.)

I'm pretty sure that's the important part. Now, for a line by line analysis.

> <?
> $suma = 0;

For starters, you won't need this line anymore. You'll see why in a minute.

> $pytanie  = "SELECT COUNT(DISTINCT x_article.ID) AS CNT ";

This line is okay. :)

> $pytanie .= "FROM x_article ";
> $pytanie .= "LEFT JOIN x_instance ";
> $pytanie .= "ON x_article.ID = x_instance.Article ";

Since an article will always belong to at least one section (right?), you don't need 
to do a left join. An equijoin will work just fine.

> $pytanie .= "LEFT JOIN x_section ";
> $pytanie .= "ON x_instance.Section = x_section.ID ";

You can also change these lines to an equijoin.

> $pytanie .= "WHERE (x_section.Status & 1) = 0 "; // not empty

If all this does is figure out if there are articles in a section, this isn't needed. 
You could find that out by joining it to x_instance. (We're doing that here anyway.)

> $pytanie .= "AND (x_section.Dept = 2 OR x_section.Dept = 5) "; // Drugs, 
> NeuroGroove

You should put first whichever of those is more likely to be true. If you get more 
articles in your drugs category than in your NeuroGroove category, leave it. If not, 
flip it. This is because most applications (and I would assume MySQL is no exception) 
don't bother to continue testing logic conditions in an OR statement once they hit a 
true one.

> $pytanie .= "AND (x_instance.Status & 255) = 0 "; // not hidden, etc

If this is all this flag does, it may be quicker to do an ENUM. You're forcing two 
operations on it here, one for the bitmask and one for the test. Alternatively, you 
could flip your flag so that 0 means hidden and 1 means visible. Then you could drop 
the "=0". Ditto for the one above.

> $pytanie .= "AND UNIX_TIMESTAMP(x_article.Date) BETWEEN " . mktime(0, 0, 1, 
> date('m'), date('d'), date('Y')) . " AND UNIX_TIMESTAMP(NOW()) ";

Here we reach what's probably causing a lot of the processing time. Date and time 
functions are relatively processor intensive. In addition, BETWEEN can't use indexes. 
Also, unless you have articles for future dates and times, you don't need to check an 
upper bound.

Because datetimes are returned as strings, you can use string functions to return just 
the date. Note that the date is all your really concerned about (again, unless the 
time really matters to you, which it doesn't seem to.) In this case, LEFT will work 
wonders.

Also, since you don't need to check for an upper bound on time, you only need to check 
to see if the date is the same.

In short, you can change these lines to:

$pytanie .= "AND LEFT(x_article.Date, 10) = CURDATE()";

If you need to keep the time, just use:

$pytanie .= "AND x_article.Date >= CURDATE() AND x_article.Date <= NOW()";

If you're looking for articles that aren't for today, simply provide the appropriate 
date instead of CURDATE() and NOW()

> $pytanie .= "GROUP BY x_article.ID";

You don't need a group by. You just want a count of the articles. The group by here is 
doing almost the same thing as the DISTINCT above, only it's adding a lot of 
processing time.

> $wynik = mysql_query($pytanie);

No problems here. :)

> while ($tmp = mysql_fetch_array($wynik))
> {
> $suma += $tmp['CNT'];
> }

With the new query, you should get your answer in one field. Those lines can change to:

$suma = mysql_result($wynik, 0, 'CNT'); // The ", 'CNT'" is actually optional.

> if ($suma) 
> {
> // pretty-printing of the result
> $dzisdodano = str_pad((string)(int)$suma, 4, '0', STR_PAD_LEFT);
> }
> else $dzisdodano = '0000';
> ?>

Do you really need that else? Won't it pad a 0 with zeros?


So, here's the query in its final form, without modifying your data structure. If this 
is a query still runs slowly and is run very often, you may want to consider trying to 
drop the second join by moving data in your database. I'm going to rearrange the WHERE 
clauses, since, depending on (or perhaps because of) how well the query optimizer 
works, you should most your most restrictive clauses first.

$pytanie = "SELECT COUNT(DISTINCT x_article.ID) FROM x_article, x_instance, x_section 
WHERE x_article.DATE = CURDATE() AND x_article.ID = x_instance.Article AND 
(x_instance.status & 255) = 0 AND x_instance.Section = x_section.ID AND 
(x_section.Dept = 2 OR x_section.Dept = 5) AND (x_section.Status & 1) = 0";

So there you go. Give it a shot and see if it runs any faster. If you need the time, 
replace that first test in the WHERE clause with the one mentioned above. If this is 
run often (or if it just takes a _really_ long time), put an index on the date field, 
if one isn't there already. Your query as it stands has to read the entire table just 
for the date field.

-- 
Bob
IQ2GI5SCP2

Things You Don't Hear Every Day, #'s 16 and 17:
A professor: "It's all right if you come to class high."
A(nother) professor: "I think base 16 is cool."
--


-- 
MySQL General Mailing List
For list archives: http://lists.mysql.com/mysql
To unsubscribe:    http://lists.mysql.com/[EMAIL PROTECTED]

Reply via email to