Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update X% of rows to A, Y% of rows to B, Z% of rows to C

I have a table like this:

Products
(
   ID int not null primary key,
   Type int not null,
   Route varchar(20) null
)

I have a list on the client in this format:

Type=1, Percent=0.4, Route=A
Type=1, Percent=0.4, Route=B
Type=1, Percent=0.2, Route=C
Type=2, Percent=0.5, Route=A
Type=2, Percent=0.5, Route=B
Type=3, Percent=1.0, Route=C
...etc

When done, I'd like to assign 40% of type 1 products to Route A, 40% to Route B and 20% to Route C. Then 50% of type 2 products to Route A and 50% of type 2 products to Route B, etc.

Is there some way to do this in a single update statement?

If not in one giant statement, can it be done in one statement per type or one statement per route? As currently we're doing one per type+route any of the above would be an improvement.

like image 537
powlette Avatar asked Jan 22 '13 20:01

powlette


1 Answers

Here's an Oracle statement that I prepared before you posted that you were using SQL-Server, but it might give you some ideas, though you will have to roll your own ratio_to_report analytic function using CTE and self-joins. We calculate the cumulative proportion of each type in the products and client route tables and do a non equi-join on the matching proportion bands. The sample data I have used has some round-offs but these will reduce for larger data sets.

Here's the setup:

create table products (id int not null primary key, "type" int not null, route varchar (20) null);
create table clienttable ( "type" int not null, percent number (10, 2) not null, route varchar (20) not null);
insert into clienttable ("type", percent, route) values (1, 0.4, 'A');
insert into clienttable ("type", percent, route) values (1, 0.4, 'B');
insert into clienttable ("type", percent, route) values (1, 0.2, 'C');
insert into clienttable ("type", percent, route) values (2, 0.5, 'A');
insert into clienttable ("type", percent, route) values (2, 0.5, 'B');
insert into clienttable ("type", percent, route) values (3, 1.0, 'C');

insert into products (id, "type", route) values (1, 1, null);
insert into products (id, "type", route) values (2, 1, null);
insert into products (id, "type", route) values (3, 1, null);
insert into products (id, "type", route) values (4, 1, null);
insert into products (id, "type", route) values (5, 1, null);
insert into products (id, "type", route) values (6, 1, null);
insert into products (id, "type", route) values (7, 1, null);
-- 7 rows for product type 1 so we will expect 3 of route A, 3 of route B, 1 of route C (rounded)

insert into products (id, "type", route) values (8, 2, null);
insert into products (id, "type", route) values (9, 2, null);
insert into products (id, "type", route) values (10, 2, null);
insert into products (id, "type", route) values (11, 2, null);
insert into products (id, "type", route) values (12, 2, null);
-- 5 rows for product type 2 so we will expect 3 of route A and 2 of route B (rounded)

insert into products (id, "type", route) values (13, 3, null);
insert into products (id, "type", route) values (14, 3, null);
-- 2 rows for product type 3 so we will expect 2 of route C

and here's the statement

select prods.id, prods."type", client.route cr from
(
select
p.id, 
p."type", 
row_number () over (partition by p."type" order by p.id) / count (*) over (partition by p."type") cum_ratio
from
products p
) prods
inner join 
(
select "type", route, nvl (lag (cum_ratio, 1) over (partition by "type" order by route), 0) ratio_start, cum_ratio ratio_end from 
(select "type", route, sum (rr) over (partition by "type" order by route) cum_ratio
from (select c."type", c.route, ratio_to_report (c.percent) over (partition by "type") rr from clienttable c))) client 
on prods."type" = client."type" 
and prods.cum_ratio >= client.ratio_start and prods.cum_ratio < client.ratio_end

This gives the following result:-

+----+------+----+
| ID | type | CR |
+----+------+----+
|  1 |    1 | A  |
|  2 |    1 | A  |
|  3 |    1 | B  |
|  4 |    1 | B  |
|  5 |    1 | B  |
|  6 |    1 | C  |
|  8 |    2 | A  |
|  9 |    2 | A  |
| 10 |    2 | B  |
| 11 |    2 | B  |
| 13 |    3 | C  |
+----+------+----+
like image 179
Lord Peter Avatar answered Nov 15 '22 07:11

Lord Peter