Update multiple columns based on different rows in another column sql server - sql

I have a table
staff Type col1 col2
1 a 0 200
1 b 50 2000
1 c 2 200
2 a 0 100
2 b 25 1000
2 c 2 200
3 a 0 150
3 b 35 1500
3 c 2 200
I am just wondering if I could update both col1 and col2 based on the values in the Type column for each staff. My logic to update would be for example for staff 1
update col1 where Type =a is 200/(2000/50) =5
update col1 where Type =b is col1 - (200/(2000/50)) =50-5 = 45
update col2 where Type =b is 2000-200 = 1800
update col2 where Type =a to 0
Please see the resultant which I need to achieve is below and updated values(*)
staff Type col1 col2
1 a 5* 0*
1 b 45* 1800*
1 c 2 200
2 a 2.5* 0*
2 b 22.5* 900*
2 c 2 200
3 a 3.5* 0*
3 b 31.5* 1350*
3 c 2 200
Is it possible to get this done without using cursors/loops/functions/ctes?

Try below code
DECLARE #myTable TABLE (
staff INTEGER NOT NULL
,Type VARCHAR(1) NOT NULL
,col1 INTEGER NOT NULL
,col2 INTEGER NOT NULL
)
INSERT INTO #myTable VALUES(1,'a',0,200),
(1,'b',50,2000),
(1,'c',2,200),
(2,'a',0,100),
(2,'b',25,1000),
(2,'c',2,200),
(3,'a',0,150),
(3,'b',35,1500),
(3,'c',2,200)
UPDATE t1
SET Col1 = CASE WHEN Type ='a'
THEN col2/(Select Col2/Col1 FROM #myTable T2 WHERE T2.Type = 'b' AND T2.staff = T1.Staff)
WHEN Type ='b'
THEN col1 - ((Select Col2 FROM #myTable T2 WHERE T2.Type = 'a' AND T2.staff = T1.Staff)/(Col2/Col1))
ELSE COl1
END ,
Col2 = CASE WHEN Type = 'b'
THEN Col2 - (Select Col2 FROM #myTable T2 WHERE T2.Type = 'a' AND T2.staff = T1.Staff)
WHEN Type = 'a'
THEN 0
Else Col2
END
FROM #MyTable t1
SELECT * FROM #myTable

You can do it in one massive query, but you will likely need a lot of fail-safes to make it foolproof. For instance, you would've to account for a value of 0 in b:col1 or b:col2 (although assuming that you only want the cross multiplication, you can do a:col2*b:col1/b:col2 and it's much simpler for the computer (divisions are a mess) on top of having 1 less exception. The drawback is when you start working with massive numbers, but I'm gonna assume that you won't go over 1000000^2 so that's kinda irrelevant. Similarly, my code is gonna assume that there is always exactly one a row and a b row.
declare #test as table(
staff int,
Type char(1),
col1 decimal(6,2),
col2 decimal(6,2)
)
insert into #test values(1,'a',0,200);
insert into #test values(1,'b',50,2000);
insert into #test values(1,'c',2,200);
insert into #test values(2,'a',0,100);
insert into #test values(2,'b',25,1000);
insert into #test values(2,'c',2,200);
insert into #test values(3,'a',0,150);
insert into #test values(3,'b',35,1500);
insert into #test values(3,'c',2,200);
UPDATE
FirstTable
SET
FirstTable.col1 = CASE FirstTable.[type] WHEN 'a' THEN JoinedTable.col1 * FirstTable.col2 / JoinedTable.col2 ELSE FirstTable.col1 - FirstTable.col1 * JoinedTable.col2 / FirstTable.col2 END,
FirstTable.col2 = CASE FirstTable.[type] WHEN 'a' THEN 0 ELSE FirstTable.col2 - JoinedTable.col2 END
FROM
#test FirstTable
INNER JOIN
#test JoinedTable
ON
FirstTable.staff = JoinedTable.staff AND JoinedTable.[Type] = CASE FirstTable.[Type] WHEN 'a' THEN 'b' ELSE 'a' END
WHERE
FirstTable.[Type] in('a','b')
The behavior is as follow : if there is a 0 in a col2 for the Type b, it crashes. If there is more than one row for one staff with Type a or b, I believe it takes one of the 2 in a non deterministic fashion, which will lead to some non deterministic and potentially faulty results. If there is no Type a or b, it skips the staff entirely.
Otherwise (aka when it works), it simply updates rows with Type a and b based on their Type. Updating both in 1 query allow you to not use temporary tables and the likes since the new data doesn't override the old until the whole query is doen.
Also, try not to use keywords like Type for your columns or you may end up with some mistakes down the line.

Related

How to select a matrix of distinct values in PL/SQL using “where” expression for each cell

I’m interested to analyze a table in Oracle which has the following form:
Column A
Column B
Column C
11
0
A
14
1
7
45
3
3
64
3
3
80
7
3
IMPORTANT:
Column A is varchar2(10)
Column B is Number(1)
Column C is Char(1)
A is primary key
B has only {0,1,3,7} distinct values
C has only {‘0’,’1’,’2’,’3’,’4’,’5’,’6’,’7’,’8’,’9’,’A’} distinct values
I want to make select command that will show matrix of distinct values where each cell will be result of expression such as
select count(*) from table where B = 1 and C = ‘A’
It means that we will get this kind of matrix:
0
1
3
7
0
1
2
3
4
5
6
7
8
9
A
For example cell(B = 3, C = ‘A’) will contain result of this command:
select count(*) from table where B = 3 and C = ‘A’
Is it possible to make this only with select command or I need to create new tables and make scripts?
I have tried to make it manually with this command
select count(*) from table where B = 1 and C = ‘A’
Just alter B and C values in this command myself and it makes me tired because it takes a lot of time. This table is enormous for this king of commands. So, I’m interested to use universal command that helps me to analyze this table
You can create additional table (or subquery or temporal table) for holding the rows:
CREATE TABLE subquery (col Char(1));
INSERT INTO subquery(col) VALUES ('0');
INSERT INTO subquery(col) VALUES ('1');
INSERT INTO subquery(col) VALUES ('2');
INSERT INTO subquery(col) VALUES ('3');
INSERT INTO subquery(col) VALUES ('4');
INSERT INTO subquery(col) VALUES ('5');
INSERT INTO subquery(col) VALUES ('6');
INSERT INTO subquery(col) VALUES ('7');
INSERT INTO subquery(col) VALUES ('8');
INSERT INTO subquery(col) VALUES ('9');
INSERT INTO subquery(col) VALUES ('A');
Then, simply write:
SELECT A.col
,SUM(CASE WHEN B.Column_B = 0 THEN 1 ELSE 0 END) AS Col0
,SUM(CASE WHEN B.Column_B = 1 THEN 1 ELSE 0 END) AS Col1
,SUM(CASE WHEN B.Column_B = 3 THEN 1 ELSE 0 END) AS Col3
,SUM(CASE WHEN B.Column_B = 7 THEN 1 ELSE 0 END) AS Col7
FROM subquery A
LEFT JOIN example_table B
ON A.col = B.Column_C
GROUP BY A.col
ORDER BY A.col
Here is the full working example.
I found it simple:
SELECT B,C, count(*) from table group by B,C
It will not show matrix, but this vector can be used to create matrix by using B and C values as indexes and count(*) as value

Count value across multiple columns

I am looking to count the number of times set of values occurred in a table. These values could occur in up to 10 different columns. I need to increment the count regardless of which column it is in. I know how I could count if they were all in the same column but not spanning multiple columns.
Values can be added in any order. I have about a thousand
Cpt1 Cpt2 Cpt3 Cpt4 Cpt5
63047 63048 63048 NULL NULL
I would want to for this row I'd expect this as the result
63047 1
63048 2
You could use a union all call to treat them as one column:
SELECT col, COUNT(*)
FROM (SELECT col1 FROM mytable
UNION ALL
SELECT col2 FROM mytable
UNION ALL
SELECT col3 FROM mytable
-- etc...
) t
GROUP BY col
It's not entirely clear what your table exactly looks like, but I'm guessing that what you're looking for is:
SELECT row_count = COUNT(*),
row_count_with_given_value = SUM ( CASE WHEN field1 = 'myValue' THEN 1
WHEN field2 = 'myValue' THEN 1
WHEN field3 = 'myValue' THEN 1
WHEN field4 = 'myValue' THEN 1 ELSE 0 END)
FROM myTable
Assuming the fieldx columns are not NULL-able, you could write it like this too:
SELECT row_count = COUNT(*),
row_count_with_given_value = SUM ( CASE WHEN 'myValue' IN (field1, field2, field3, field4) THEN 1 ELSE 0 END)
FROM myTable
Something like this might work (after adapting to your value domain and data types):
create table t1
(i1 int,
i2 int,
i3 int);
insert into t1 values (1,0,0);
insert into t1 values (1,1,1);
insert into t1 values (1,0,0);
declare #i int = 0;
select #i = #i + i1 + i2 + i3 from t1;
print #i;
drop table t1;
Output is: 5
Many databases support lateral joins, of one type of another. These can be used to simplify this operation. Using the SQL Server/Oracle 12C syntax:
select v.cpt, count(*)
from t cross apply
(values (cpt1), (cpt2), . . .
) v(cpt)
where cpt is not null
group by v.cpt;

Checking to see if multiple conditions are true within a group in SQL

Lets say I have the following table depicting a one-many relationship
col1 | col2
-------------
1 | foo
1 | bar
2 | foo
3 | buzz
I need to group by col1 and I need a boolean indicating whether or not there is both a mapping to 'foo' and a mapping to 'bar'.
So, the final result set would be
col1 | foobar
-------------
1 | 1
2 | 0
3 | 0
What is the best way to achieve this in T-SQL?
I've been trying something roughly equivalent to the following query with no luck.
SELECT
col1
, (
MAX (
CASE WHEN
COL2 = 'foo'
THEN 1 ELSE 0
END) = 1
AND
MAX (
CASE WHEN
COL2 = 'bar'
THEN 1 ELSE 0
END) = 1
)
FROM
table
GROUP BY
col1
EDIT:
To clarify, this table is a simplification.
I am looking for a solution to the general problem of having a one-many mapping and needing to produce a new 1-1 mapping with a Boolean indicating if a variable number of predicates are true of the different elements in the groups in the co-domain. (grouped by the fact that the same element maps to them)
Also, I should clarify that these various predicates could be anything.
For example, maybe I want to see if at least one of the columns in one of the rows = 'foo' and also that a different column in a (possibly different) row within the same group is between a certain set of numeric values.
What about this?
EDIT: Better use COUNT(DISTINCT col2)
DECLARE #tbl TABLE(col1 INT,col2 VARCHAR(100));
INSERT INTO #tbl VALUES
(1,'foo')
,(1,'bar')
,(2,'foo')
,(3,'buzz');
SELECT col1
,COUNT(DISTINCT col2)-1
FROM #tbl
GROUP BY col1
UPDATE:
If you try it like this, you would even see, which values are there. If you are only interested in "one or many" you might check for a comma in the returned string:
DECLARE #tbl TABLE(col1 INT,col2 VARCHAR(100));
INSERT INTO #tbl VALUES
(1,'foo')
,(1,'bar')
,(2,'foo')
,(3,'buzz');
SELECT outerTbl.col1
,STUFF
(
(
SELECT DISTINCT ', ' + col2
FROM #tbl AS innerTbl
WHERE innerTbl.col1=outerTbl.col1
FOR XML PATH('')
),1,2,''
)
FROM #tbl AS outerTbl
GROUP BY outerTbl.col1
This is the result:
1 bar, foo
2 foo
3 buzz
Here is a simple option
select col1
,MAX(case when col2='foo' then 1 else 0 end)*MAX(case when col2='bar' then 1 else 0 end) foobar
from #tbl
group by col1
Try this:
select
col1,
case when foo > 0 and bar > 0 then true else false end foobar
from (
select
col1,
sum(case when col2 = 'foo' then 1 end) foo,
sum(case when col2 = 'bar' then 1 end) bar
from table
group by col1) x

insert select alias value into column

Not sure if this is the best title, but i want to select string values into an int column of a new table (the reason is to use keys with int data types rather than strings, so there are more columns not shown in this example)
table1.key1 table2.key2
a 1
b 2
c 3
a 1
one way i can do this is as follows but the syntax is very very long in some scenarios
insert into table2 (key2)
select 1
from table1
where key1 = 'a'
insert into table2 (key2)
select 2
from table1
where key1 ='b'
etc...
can someone show me how i could use a syntax that is shorter? also i have to keep identity insert set to off so an update statement will not work from what i understand.
SQL Fiddle Demo
Use a CASE expresion
insert into table2 (key2)
select CASE WHEN key1 = 'a' THEN 1
WHEN key1 = 'b' THEN 2
WHEN key1 = 'c' THEN 3
.....
ELSE -1
END as key2
from table1

Get addition of row values based on coresponding row values SQL Server

I have data base table(dbo.TestTable_1) like following:
Result Value1 Value2
Y A 1
A 0
B 1
A 1
B 0
I have to read top 1 of column "Value1" and check, in which row(of column Value1) matches the value. get the corresponding rows values and add it.If my addtion is > 0 then I want to return Yes, else return NO.
For example:
"select top 1 Value1 from dbo.TestTable_1" will return A.
Then I need to find the row number where A exists in "Value1". That is (here it is at), Row number 1,2 and 4. Then I want to add the corresponding values in "Value2" column. That is 1+0+1 = 2(this is greater then 0) then I want to return YES.
My output table should look like following:
Result Value1 Value2
YES A 1
A 0
B 1
A 1
B 0
I have tried following query to achieve this. But I'm unable to proceed further.
declare #val1 int
set #val1 = (select top 1 Value1 from dbo.TestTable_1)
create table #Temp
(
Val1 int
)
Insert into #Temp
select Value1
from dbo.TestTable_1
select * from #Temp
I hope, my explanation is understood. Please help.Thanks.
You are close, you just need to sum value2. You don't need a temp table either:
declare #val1 int
set #val1 = (select top 1 Value1 from dbo.TestTable_1)
select CASE WHEN SUM(Value2) > 0 THEN 'Yes' ELSE 'No' END
from dbo.TestTable_1
WHERE value1 = #val1