Reputation: 5918
I could fall back to a procedure with a while loop (maybe, I've never tried to do this before) but there must be a faster way.
[backspaced stuff - I'll just show describes]
CREATE TABLE `table` (
`ZoneId` int(10) unsigned NOT NULL AUTO_INCREMENT,
`VolumeId` int(10) unsigned NOT NULL,
`TypeId` int(10) unsigned NOT NULL,
`ExtraTypeInfo` int(10) unsigned NOT NULL,
`Time` int(10) unsigned NOT NULL,
`StartIndex` int(10) unsigned NOT NULL,
`EndIndex` int(10) unsigned NOT NULL,
`SectionId` int(10) unsigned NOT NULL,
PRIMARY KEY (`ZoneId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
StartIndex
and EndIndex
denote a range, it is saying everything from start until the end has the properties this record describes (say a range covers 300,000 this is better than 300,000 rows, also the items don't need IDs, their position is their ID)
Some indices will be covered twice, for example the range [1,20]
and [1,1]
with TypeId
0 means that 1
has been (whatever it denotes) twice, and 2-20 inclusive just once, the rest 0 times.
Rather than reading the table and keeping a manual tally I'd rather shift the work to MySQL, I (sadly) must use PHP and it uses too much memory if I use an array or object per entry, and takes too long if I use a bitfield.
Rather than being tossed a query and using it I'd rather learn how to do this, so I would love a solution has three columns:
section | index | count0 | count1
Where count0 is the number of times intervals with TypeId
0 has covered the index, and count1
where it is 1. I have asked for both to prevent solutions where this'd be done as two queries. (In the field this will be subject to a given VolumeId)
The length of a section is of course known
Suppose we have (ignoring ZoneId
,ExtraTypeInfo
and Time
for simplicity) with this dataset:
VolId TypeId Start End Section
1 0 1 2 1
1 0 2 2 1
2 0 1 1 1
1 1 2 3 1
1 0 1 2 2
To output (subject to VolId = 1)
sec ind c0 c1
1 1 1 0
1 2 2 1
1 3 1 1
1 4 0 0 <--notice this, we do know the length of sec 1 and 4 was covered by nothing
2 1 1 0
2 2 1 0
2 3 0 0
2 4 0 0
2 5 0 0
Indices may start at 0 or 1, it is easy to change and as yet undecided (starting at 1 is most natural for the application, 0 helps only with the code)
I hope SQL can do this. I will carry on reading but I'm not sure what to do, as I mentioned at the top I am thinking of a procedure that runs over all sections that calls a procedure to generate lists from 1 to length, and then searching for each of those and hoping MySQL's optimiser spots what is going on.
Upvotes: 3
Views: 61
Reputation: 125875
Further to our conversation, I suggest that you create a permanent number_table
that contains every possible index value:
CREATE TABLE number_table (ind SMALLINT UNSIGNED PRIMARY KEY)
SELECT bF.v|bE.v|bD.v|bC.v|bB.v|bA.v|b9.v|b8.v
|b7.v|b6.v|b5.v|b4.v|b3.v|b2.v|b1.v|b0.v ind
FROM
(SELECT 0 v UNION ALL SELECT 1<<0x0) b0,
(SELECT 0 v UNION ALL SELECT 1<<0x1) b1,
(SELECT 0 v UNION ALL SELECT 1<<0x2) b2,
(SELECT 0 v UNION ALL SELECT 1<<0x3) b3,
(SELECT 0 v UNION ALL SELECT 1<<0x4) b4,
(SELECT 0 v UNION ALL SELECT 1<<0x5) b5,
(SELECT 0 v UNION ALL SELECT 1<<0x6) b6,
(SELECT 0 v UNION ALL SELECT 1<<0x7) b7,
(SELECT 0 v UNION ALL SELECT 1<<0x8) b8,
(SELECT 0 v UNION ALL SELECT 1<<0x9) b9,
(SELECT 0 v UNION ALL SELECT 1<<0xA) bA,
(SELECT 0 v UNION ALL SELECT 1<<0xB) bB,
(SELECT 0 v UNION ALL SELECT 1<<0xC) bC,
(SELECT 0 v UNION ALL SELECT 1<<0xD) bD,
(SELECT 0 v UNION ALL SELECT 1<<0xE) bE,
(SELECT 0 v UNION ALL SELECT 1<<0xF) bF;
DELETE FROM number_table WHERE ind = 0;
Then you can make an outer join between this table and your table
above, limiting the results by the maximum length of the relevant section:
SELECT s.SectionId,
nt.ind,
IFNULL(SUM(t.TypeID=0),0) c0,
IFNULL(SUM(t.TypeID=1),0) c1
FROM sections s
JOIN number_table nt
ON nt.ind <= s.Length
LEFT JOIN `table` t
ON t.SectionId = s.SectionId
AND nt.ind BETWEEN t.StartIndex AND t.EndIndex
AND t.VolumeId = 1
GROUP BY s.SectionId, nt.ind
See it on sqlfiddle.
Upvotes: 1