Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- drop table if exists combiner;
- create table combiner (owner integer, attribute varchar(16), value varchar(16));
- insert into combiner values
- ('10','COLOR','BLUE'),
- ('10','COLOR','RED'),
- ('10','COLOR','GREEN'),
- ('10','SIZE','BIG'),
- ('20','COLOR','GREEN'),
- ('20','SIZE','MEDIUM'),
- ('20','MEMORY','16G'),
- ('20','MEMORY','32G'),
- ('30','COLOR','RED'),
- ('30','COLOR','BLUE'),
- ('30','MEMORY','64G'),
- ('40','COLOR','BLUE'),
- ('40','COLOR','RED'),
- ('40','COLOR','GREEN'),
- ('40','SIZE','BIG'),
- ('40','SIZE','MEDIUM'),
- ('40','MEMORY','16G'),
- ('40','MEMORY','32G'),
- ('40','CPU','FAST')
- ;
- -- selects the number of possible combinations for an owner
- SELECT
- EXP(SUM(LOG(COALESCE(cnt)))) -- calculates number of different combinations for attributes
- FROM
- (SELECT attribute, COUNT(*) AS cnt FROM combiner
- WHERE owner = 10 -- for testing
- GROUP BY
- attribute
- ) q1
- ;
- -- we want to operate on thoose numbers from 1..max
- -- it would be easy with a stored procedure, but can also be done in purely SQL
- -- please note, that I would never use this code in production
- -- to create this counter, the idea is that n < COUNT(combiner)^2
- -- so: create a cross join, and an iterator... for a fixed N:
- SET @rank=0;
- SELECT
- @rank:=@rank+1 AS rank
- FROM
- combiner c1,
- combiner c2
- WHERE @rank < 5; -- 5 is N
- -- So combine theese two, to create a loop from 1..N:
- -- also note: we need N*M rows, where N is the number of combinations,
- -- and M is the number of attributes. So combine!
- SET @rank=0;
- SELECT
- @rank:=@rank+1 AS rank
- FROM
- combiner c1,
- combiner c2
- WHERE @rank+1 < (
- SELECT
- (
- EXP(SUM(LOG(COALESCE(cnt))))
- * ( SELECT COUNT(DISTINCT attribute) FROM combiner q1 WHERE owner = 10 ) -- number of attributes, fixed 10 is for testing
- )
- FROM
- (SELECT
- attribute,
- COUNT(*) AS cnt
- FROM combiner c3
- WHERE owner = 10 -- for testing
- GROUP BY
- attribute
- ) q2
- )
- ;
- -- now we have the correct amount of rows, the only thing left is to select the wanted rows...
- -- to do that, we need to convert our number back to some row information
- -- first, the attribute name:
- -- we have a number N. for every N, N % attribute_count is the attribute we want to display.
- -- which would be easy to select if we could write a custom expression to offset, but we can't.
- -- also, we may only define variables at the start of our query, not in beetween - because of this,
- -- it is hard to reset a variable inside a query.
- -- but not inpossible. For example, see the following simple demonsration:
- SET @example = 15;
- SELECT
- @example, owner
- FROM
- combiner
- WHERE
- @example := (@example := 0)
- UNION
- SELECT @example := @example + 1, owner
- FROM combiner
- ;
- -- so, to select the Nth attribute, use:
- SET @attribute_counter = 0;
- SELECT attribute
- FROM
- (
- SELECT
- @attribute_counter AS attribute_counter, attribute
- FROM
- combiner
- WHERE
- @attribute_counter := (@attribute_counter := 0)
- UNION
- SELECT @attribute_counter := @attribute_counter + 1, attribute
- FROM (
- SELECT DISTINCT attribute
- FROM combiner
- WHERE owner = 10 -- for testing
- ORDER BY attribute) q3
- ) q4
- WHERE attribute_counter = 2 -- in this example, N = 2
- ;
- -- now combine our counter query and this attribute selector, to solve part of the problem
- SET @rank=0, @attribute_counter = 0;
- SELECT
- @rank AS rank,
- (SELECT COUNT(DISTINCT attribute) FROM combiner WHERE owner = 10 ORDER BY attribute) AS attribute_count,
- (SELECT attribute
- FROM
- (
- SELECT
- @attribute_counter AS attribute_counter, attribute
- FROM
- combiner
- WHERE
- @attribute_counter := (@attribute_counter := 0 )
- UNION
- SELECT @attribute_counter := @attribute_counter + 1, attribute
- FROM (SELECT DISTINCT attribute FROM combiner WHERE owner = 10 ORDER BY attribute) q3
- ) q4
- WHERE attribute_counter = MOD(
- rank,
- attribute_count
- ) + 1
- ) attribute,
- @rank := @rank + 1 AS rank2
- FROM
- combiner c1,
- combiner c2
- WHERE @rank+1 < (
- SELECT
- (
- EXP(SUM(LOG(COALESCE(cnt))))
- * ( SELECT COUNT(DISTINCT attribute) FROM combiner q1 WHERE owner = 10 ) -- number of attributes, fixed 10 is for testing
- )
- FROM
- (SELECT
- attribute,
- COUNT(*) AS cnt
- FROM combiner c3
- WHERE owner = 10 -- for testing
- GROUP BY
- attribute
- ) q2
- )
- ;
- -- the value of the attribute, that's also can be calculated, but it's harder
- -- we are in the Nth iteration
- -- and at the moment processing the attribute_number attribute
- -- there are attribute_count attributes
- -- we have completed completed_count attributes
- -- also, the attributes AFTER the current have Vx possible values combined
- -- which means, this attribute will change in every Vx row
- -- and this attribute has Y possible values, let Vy = Vx * Y
- -- which is basically Vx INCLUDING this column
- -- so N % V gives us a great number to use.
- -- First: let's compute V based on our previous attribute selector query.
- SET @after_counter = 0;
- SELECT
- EXP(SUM(LOG(COALESCE(value_count)))) AS after_rows
- FROM
- (
- SELECT
- DISTINCT
- attribute,
- (SELECT COUNT(value) FROM combiner c2 WHERE owner = 10 AND c1.attribute = c2.attribute) value_count
- FROM combiner c1
- WHERE attribute IN
- (
- SELECT attribute
- FROM
- (
- SELECT
- @after_counter AS after_counter, attribute
- FROM
- combiner
- WHERE
- @after_counter := (@after_counter := 0)
- UNION
- SELECT @after_counter := @after_counter + 1, attribute
- FROM (
- SELECT DISTINCT attribute
- FROM combiner
- WHERE owner = 10 -- for testing
- ORDER BY attribute
- ) q5
- ) q6
- WHERE after_counter > 1
- )
- ) q7
- ;
- -- for owner = 10 in the example
- -- for after_counter = 0, it returns 3. But there is no field zero
- -- for 1, it retuns 1, since color changes every time
- -- for 2, it retuns 0: there is just one size, it never changes
- -- for an advanced case, see for my owner = 40 example, which has 4 attributes, with a single-value one inside
- -- next step: we want value N % V from the attribute. It's easy, using the same way as for the attributes
- -- and now we know both N and V, so just combine...
- -- and final step: refactor the entire query, to run for every owner instead of this fixed example
- -- it will be a lot more uglier and larger, but it will run
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement