Assign Integer Value to Row Based on Interval - Sql

Assign Integer Value to Row Based on Interval - Sql

I have a MySQL dataset that looks like this:

a     b
.32   .72
.41   .80
.28   .64
.31   .80

And I want to assign values (c) to each row based on the conditions of a and b:

.3 < a < .4 and .71 < b < .83 = 1

.4 < a < .5 and .71 < b < .83 = 2

.2 < a < .3 and .58 < b < .77 = 3

and so on. This would result in my table looking like this:

a     b     c
.32   .72   1
.41   .80   2
.28   .64   3
.31   .80   1

How would I do this? I have tried a case when() statement but that didn't work since I don't know how/if it is possible to have one of those with more than one case.

7

3 Answers

I don't fully understand this: I have tried a case when() statement but that didn't work since I don't know how/if it is possible to have one of those with more than one case.

This will get you the output you described however. I'm a bit lazy/tired so I'll just paste the output from SQL Fiddle:

SQL Fiddle

MySQL 5.5.32 Schema Setup:

CREATE TABLE Table1
    (`a` decimal(2,2), `b` decimal(2,2))
;

INSERT INTO Table1
    (`a`, `b`)
VALUES
    (.32, .72),
    (.41, .80),
    (.28, .64),
    (.31, .80)
;

alter table table1 add column c int;

update table1
set c = 
case 
  when (.3 < a and a < .4) and (.71 < b and b < .83) then  1
  when (.4 < a and a < .5) and (.71 < b and b < .83) then  2
  when (.2 < a and a < .3) and (.58 < b and b < .77) then  3
end;

Query 1:

select * from table1

Results:

|    A |    B | C |
|------|------|---|
| 0.32 | 0.72 | 1 |
| 0.41 |  0.8 | 2 |
| 0.28 | 0.64 | 3 |
| 0.31 |  0.8 | 1 |
4

You don't say what SQL implementation you're using.

If you're using SQL Server, you can add a computed column like this:

create table foo
(
  int a not null ,
  int b not null ,

  c as case
       when a > 0.3 and a < 0.4 and b > 0.71 and b < 0.83 then 1
       when a > 0.4 and a < 0.5 and b > 0.71 and b < 0.83 then 2
       when a > 0.2 and a < 0.3 and b > 0.58 and b < 0.77 then 3
       else null // anything that doesn't match one of the above tests
       end ,

)

If you can't do something like that, you could create a view:

create table foo
(
  int a not null ,
  int b not null ,
)
create view foo_view as
select foo.* ,
       case
       when a > 0.3 and a < 0.4 and b > 0.71 and b < 0.83 then 1
       when a > 0.4 and a < 0.5 and b > 0.71 and b < 0.83 then 2
       when a > 0.2 and a < 0.3 and b > 0.58 and b < 0.77 then 3
       else null // anything that doesn't match one of the above tests
       end as c

That ensures your computed c is always in synch with the underlying data.

If you go the update route, that version isn't much harder:

update foo 
set c = case
        when a > 0.3 and a < 0.4 and b > 0.71 and b < 0.83 then 1
        when a > 0.4 and a < 0.5 and b > 0.71 and b < 0.83 then 2
        when a > 0.2 and a < 0.3 and b > 0.58 and b < 0.77 then 3
        else null // anything that doesn't match one of the above tests
        end

Though you might consider putting an update trigger on the table to automagickally update c then a or b changes value.

1

I would create a secondary table and use it to drive the update.

Generally, try to parametrize things like this into tables, rather than making a big honking sql.

That still leaves you with the question of whether there is a contradiction in your criteria (i.e. multiple outcomes possible, no outcomes, etc...) but is cleaner in terms of sql and in terms of maintainability.

I've handled no outcome/multiple outcomes with exists and limit 1 below, respectively so that at least they don't error out.

drop table tgt;
create table tgt(a float, b float, calc int);

drop table range;
create table range(a_low float, a_high float, 
b_low float, b_high float, calc float);

select * from tgt;

insert into tgt (a,b, calc) values(.32,   .72, 0);
insert into tgt values(.41,   .80, 0);
insert into tgt values(.28,  .64, 0);
insert into tgt values(.31,   .80, 0);

/*
3 < a < .4 and .71 < b < .83 = 1

.4 < a < .5 and .71 < b < .83 = 2

.2 < a < .3 and .58 < b < .77 = 3
*/

insert into range (a_low, a_high, b_low, b_high, calc) 
values (.3, .4, .71, .83, 1);
insert into range (a_low, a_high, b_low, b_high, calc) 
values (.4, .5, .71, .83, 2);
insert into range (a_low, a_high, b_low, b_high, calc) 
values (.2, .3, .58, .77, 3);


select * from tgt;

update tgt
set calc =
(select calc 
from range 
where tgt.a 
between range.a_low and range.a_high 
and tgt.b between range.b_low and range.b_high 
/* limit is to avoid 
if error if multiple results 
- picks only one
*/
limit 1)
where 
/* and exists avoids it if there are no results */
exists
(select calc 
from range 
where tgt.a 
between range.a_low and range.a_high 
and tgt.b between range.b_low and range.b_high)

;

select * from tgt;

in postgresql this is the result:

0.32; 0.72; 1

0.41; 0.8; 2

0.28; 0.64; 3

0.31; 0.8; 1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Elena Rostova
Author

Elena Rostova

Elena Rostova holds a Master's degree in Public Health Journalism. She covers groundbreaking medical research, holistic wellness trends, mental health awareness, and nutritional science.