Nok Imchen
Nok Imchen

Reputation: 2842

Speed up MySQL query made in PHP

Say, i have a table a given here. The table has the following structure (day of test, name of student, and marks obtained)

D       NAME    MARKS
2001-01-01  a   1
2001-01-04  a   4
2001-01-06  a   3
2001-01-08  a   3
2001-01-01  b   1
2001-01-10  b   15
2001-01-01  c   1
2001-01-06  c   2
2001-01-08  c   5
2001-01-10  c   7

I'd like to update the table by give 0 marks to those students who do not have an entry for each test. The update table should look like this

D   NAME    MARKS
2001-01-01  a   1
2001-01-02  a   0
2001-01-04  a   4
2001-01-06  a   3
2001-01-08  a   3
2001-01-02  a   0
2001-01-01  b   1
2001-01-02  b   0
2001-01-04  b   0
2001-01-06  b   0
2001-01-08  b   0
2001-01-10  b   15
2001-01-01  c   1
2001-01-02  c   2
2001-01-04  c   0
2001-01-06  c   0
2001-01-08  c   5
2001-01-10  c   7

So far, the only solution(very very slow query) i can figure out is:

SELECT DISTINCT(D) FROM tableA;
SELECT DISTINCT(NAME) FROM tableA;

Using PHP, a sql query is made inside a nested loop

INSERT IGNORE (D,NAME,MARKS)($D,$NAME,0);

However, the whole code takes too much time (in minutes) as there are more than 50k rows.

Any better suggestions?

Upvotes: 1

Views: 463

Answers (1)

Kickstart
Kickstart

Reputation: 21533

Possibly do a cross join of the possible dates and possible names and left join that against the current results:-

INSERT INTO A (D, name, marks)
SELECT Dates.D, Names.name, 0
FROM (SELECT DISTINCT D FROM A) Dates
CROSS JOIN  (SELECT DISTINCT name FROM A) Names
LEFT OUTER JOIN A
ON Dates.D = A.D AND Names.name = A.name
WHERE A.name IS NULL

Note this does assume that at least one person has a mark for each day.

It would be better if you had a table of names and a table of dates, and just used the ids of these rows in the table A

If you want to do this for a range of dates, even if nobody got a mark that day:-

INSERT INTO A (D, name, marks)
SELECT Dates.aDate, Names.name, 0
FROM 
(
    SELECT DATE_ADD('2001-01-01', INTERVAL units.i + tens.i * 10 + hundreds.i * 100 DAY) AS aDate
    FROM (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units
    CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) tens
    CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) hundreds
    HAVING aDate BETWEEN '2001-01-01' AND '2001-12-30'
) Dates
CROSS JOIN  
(
    SELECT DISTINCT name 
    FROM A
) Names
LEFT OUTER JOIN A
ON Dates.aDate = A.D AND Names.name = A.name
WHERE A.name IS NULL

Upvotes: 4

Related Questions