Swap column value of same table in single SQL query - sql

I have requirement to update column value of two rows with different keys. Constraint is that i want to do this in single query.
For example:
Coll1 Coll2
---------------
A 1
B 2
C 3
Should be like
Coll1 Coll2
--------------
A 3
B 2
C 1

using a case expression:
update t
set Coll2 = case when Coll1 = 'A' then 3 else 1 end
where Coll1 in ('A','C')
rextester demo: http://rextester.com/HUBDAP9516
returns:
+-------+-------+
| Coll1 | Coll2 |
+-------+-------+
| A | 3 |
| B | 2 |
| C | 1 |
+-------+-------+
update for a parameterized version:
declare #key1 char(1) = 'A';
declare #key2 char(1) = 'C';
update t
set t.Coll2 = x.Coll2
from t
inner join t x
on t.Coll1 <> x.Coll1
and t.Coll1 in (#key1,#key2)
and x.Coll1 in (#key1,#key2)
rextester demo: http://rextester.com/PKQSAV63963
returns:
+-------+-------+
| Coll1 | Coll2 |
+-------+-------+
| A | 3 |
| B | 2 |
| C | 1 |
+-------+-------+

Perhaps you mean a single transaction. Either way, I don't understand why--but since that is what you want, here's an easy way.
declare #table table (Col1 char(1), Col2 int)
insert into #table
values
('A',1),
('B',2),
('C',3)
update #table
set
Col2 = case
when Col1 = 'A' then 3
when Col1 = 'C' then 1
end
where Col1 in ('A','C')
select * from #table

BEGIN TRANSACTION
UPDATE t SET Coll2 = 3 WHERE Coll1 = 'A'
UPDATE t SET Coll2 = 1 WHERE Coll1 = 'C'
COMMIT

I'd like to try the following answer without hard-code col2
create table #t (col1 char(301), col2 int);
go
insert into #t (col1, col2)
values ('A', 1), ('B', 2), ('C', 3)
; with c as (
select col1, col2
from #t
)
update t
set t.col2 = c.col2
from #t t
inner join c
on abs(ascii(t.col1) - ascii(c.col1))=2
select * from #t

This is a generic solution where you have values to switch as a parameter or as a subquery.
It also works con char data, but need some tweaking on string manipulation.
declare #table table (Col1 char(1), Col2 int)
insert into #table
values
('A',1),
('B',2),
('C',3)
declare #swap1 char='A'
declare #swap2 char='C'
update #table
set col2 = (select sum(col2) from #table
where col1 in (#swap1,#swap2))-col2
where col1 in (#swap1,#swap2)

Related

Make a select query with stored procedure from multiple independent tables

Table A
id | food_id | price
1 | 3 | 5
2 | 7 | 9
3 | 3 | 8
Table B
id | drink_id | price | type_id
1 | 8 | 8 | 3
2 | 6 | 9 | 3
3 | 6 | 10 | 1
Table C
id(food_id) | Name
3 | Banana
7 | Strawberry
I have 3 tables like this. I want the result of the query written with the stored procedure to be as follows.
column 1
13 (select sum(price) from tableA where food_id = 3)
column 2
2 (Select count(*) from tableB where drink_id = 6)
column 3
9 (Select sum(price) from tableB where drink_id = 6 and type_id = 3)
column 4
Banana (Select Name from tableA a left join tableC c On a.id = c.id) where a.id = 1)
Suppose there are millions of rows of data in these tables. How to write the best stored procedure?
NOTE: Not validated as I don't have your tables, but this format should work once you put it into an SP.
-- put this into an SP
-- delcare varaibles, probably should change them to match results (could be decimal or int depending on what your SUM does)
DECLARE #SumPriceFood AS VARCHAR(50)
DECLARE #CountDrink AS VARCHAR(50)
DECLARE #SumPriceDrink AS VARCHAR(50)
DECLARE #Name AS VARCHAR(50)
-- get your data (not tested these as they are your tables)
SELECT #SumPriceFood = SUM(price) from tableA where food_id = 3
SELECT #CountDrink = COUNT(*) from tableB where drink_id = 6
SELECT #SumPriceDrink = sum(price) from tableB where drink_id = 6 and type_id = 3
SELECT #Name = Name from tableA a left join tableC c On a.id = c.id where a.id = 1
-- now just select the variable values you populated above for your results
SELECT #SumPriceDrink AS SumPriceDrink, #CountDrink AS CountDrink, #SumPriceDrink AS SumPriceDrink, #Name AS Name
you can use your queries as subqueries.
For an Stored procedure it isn't enough but who knows what ypu need it for
DECLARE #food_id INTEGER = 3;
DECLARE #drink_id int = 6;
DECLARE #type_id INTEGER = 3;
DEClARE #a_id int = 1;
SELECT
(select sum(price)from tableA where food_id=#food_id) col1,
(Select count(*) from tableB where drink_id=#drink_id) col2,
(Select sum(price) from tableB where drink_id=#drink_id and type_id=3) col3,
(Select Name from tableA a left join tableC c On a.id = c.id where a.id = #a_id) col4
col1 | col2 | col3 | col4
---: | ---: | ---: | :-----
13 | 2 | 9 | Banana
CREATE PROCEDURE Getdata
#food_id AS INTEGER,#drink_id int,#type_id INTEGER ,#a_id int
AS
SELECT
(select sum(price)from tableA where food_id=#food_id) col1,
(Select count(*) from tableB where drink_id=#drink_id) col2,
(Select sum(price) from tableB where drink_id=#drink_id and type_id=3) col3,
(Select Name from tableA a left join tableC c On a.id = c.id where a.id = #a_id) col4
EXEC Getdata #food_id = 3,#drink_id = 6,#type_id = 3,#a_id = 1;
col1 | col2 | col3 | col4
---: | ---: | ---: | :-----
13 | 2 | 9 | Banana
db<>fiddle here

How to add items from another table based on a string aggregated column

I have 2 tables like this
[Table 1]:
|cust_id| tran |item |
| ------| -----|-------
| id1 | 123 |a,b,c |
| id2 | 234 |b,b |
| id3 | 345 |c,d,a,b|
[Table 2]:
| item. | value |
| ----- | ----- |
| a | 1 |
| b | 2 |
| c | 3 |
| d | 4 |
I want to create a target value by doing a lookup from table 2 in table 1 using big query.
|cust_id| tran.|item |target|
| ------| -----|------|------|
| id1 | 123 |a,b,c | 6
| id2 | 234 |b,b | 4
| id3 | 345 |c,d,a,b| 10
What can I try next?
Consider below simple approach
select *,
( select sum(value)
from unnest(split(item)) item
join table2
using (item)
) target
from table1
if applied to sample data in your question - output is
Try the following:
select t1.cust_id
, t1.tran
, t1.item
, sum(t2.value) as target
from table_1 t1
, UNNEST(split(t1.item ,',')) as item_unnested
LEFT JOIN table_2 t2
on item_unnested=t2.item
group by t1.cust_id
, t1.tran
, t1.item
With your data it gives the following:
Create a center table that splits the item column values on rows and join that table with table2.
Try following
--Cursor is used to split the item data row by row
--#temp is a temporary table
create table #temp (id varchar(10), trans varchar(10), item varchar(10), item1 varchar(10));
DECLARE #item varchar(10);
DECLARE #id varchar(10);
DECLARE #trans varchar(10);
DECLARE item_cusor CURSOR FOR
SELECT *
FROM table1;
OPEN item_cusor
FETCH NEXT FROM item_cusor
INTO #id,#trans,#item
WHILE ##FETCH_STATUS = 0
BEGIN
insert into #temp
SELECT #id,#trans,#item,*
FROM STRING_SPLIT (#item, ',')
FETCH NEXT FROM item_cusor
INTO #id,#trans,#item
END
CLOSE item_cusor;
DEALLOCATE item_cusor;
--select * from temp
select t.id as cust_id, t.trans,t.item , sum(cast(t2.value as int)) as target
from #temp t
JOIN table2 t2
on t.item1=t2.item
group by t.id, t.trans,t.item;
Cursors: https://www.c-sharpcorner.com/article/cursors-in-sql-server/
Temporary tables: https://www.sqlservertutorial.net/sql-server-basics/sql-server-temporary-tables/
String split function: https://learn.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql

db2 update multiple field for a record

I have a table with 1 row and 2 columns: col1 and col2,
--------------------------------------------------
| col1 | col2 |
--------------------------------------------------
| 1 | 2 |
--------------------------------------------------
now I want to update the row record:
db2 "update myschema.mytable set col1=3, col2=col1"
I expect the result to be col1=3 and col2=3, but it gives me col1=3,col2=1
how do I update one column base on a newly updated column?
Try This:
DECLARE #A INT
SELECT #A = Col1 FROM Mytable
UPDATE MyTable SET Col1 = 4, Col2 = #A

SQL Column title from another table

I have two tables
Index | Name | GroupIndex \\table1
1 | A | 1
2 | A | 1
3 | A | 2
Index | GroupName \\table2
1 | G1
2 | G2
I would like my result to be
Index | Name | G1 | G2
1 | A | 2 | 1
where I need to display the number of entries of Name grouped by GroupIndex, but in a column format.
I couldn't find a way to add columns dynamically, from another table. Can you help me out?
create table #table1 ([Index] int,Name char(1), GroupIndex int)
create table #table2 ([Index] int,GroupName char(2))
insert into #table1 values
(1,'A',1),
(2,'A',1),
(3,'A',2)
insert into #table2 values
(1,'G1'),
(2,'G2')
--select * from #table1
--select * from #table2
select * from
(select t1.[GroupIndex] as [Index], t1.Name, t2.[Index] as gi, t2.GroupName
from #table1 t1
inner join #table2 t2 on t2.[Index] = t1.[Index]) as s
PIVOT
(max(gi) FOR GroupName in (G1,G2)) as pvt
--RESULTS--
Index Name G1 G2
1 A 1 2
create a pivot that group your user by name then count their groups using sum function for each group.
SELECT
Sum(case when tbl2.[index]=1 THEN 1 ELSE 0 end ) AS G1,
SUM(case when tbl2.[index]=2 THEN 1 ELSE 0 end ) AS G2
FROM tbl1
join tbl2 on tbl2.[index] = tbl1.GroupIndex group by tbl1.Name

Insert when NOT matched - T/SQL

I have 3 tables with same 5 columns (+ 1 flag column in T2 which we will use later...let's focus on 5 columns for this example).
T1, T2, and T3.
1st column in all three tables are Key Columns.
Let's say there are 5 records in T1 and T2.
4 records in T1 and T2 match for all columns.
5th record has 4 matching columns (1 key + 3 non-key). This means, T1 and T2 has 1 non-matching non-key column for the 5th record.
I want to do nothing with the first 4 columns
I want to insert the 5th column from T2 to T3 and update the 6th column of T2 to TRUE.
How does one go about doing this? Merge query won't work as it works with 2 tables only as far as I know...correct me if I am wrong.
Just a note: these tables have actually over 100 columns (same columns) sooo...hehe
ThanX
UPDATE: I wish to still pass the values for the first 4 columns from T2 to T3...not just the column that changed.
-- Not tuned to any particular syntax, may need to be tweaked.
T1([a],b,c,d,e);
T2([a],b,c,d,e,f);
T3([a],b,c,d,e);
---------
-- SQL --
---------
BT;
INSERT INTO T3
SELECT
T2.a,
CASE WHEN (T1.b <> T2.b) THEN T2.b ELSE null,
CASE WHEN (T1.c <> T2.c) THEN T2.c ELSE null,
CASE WHEN (T1.d <> T2.d) THEN T2.d ELSE null,
CASE WHEN (T1.e <> T2.e) THEN T2.e ELSE null
FROM
T1,
T2
WHERE T1.a = T2.a
AND (
T1.b <> T2.b
OR T1.c <> T2.c
OR T1.d <> T2.d
OR T1.e <> T2.e
);
UPDATE T2
SET T2.f = true
T1.a IN (
SEL T1.a
FROM T1, T2
WHERE T1.a = T2.a
AND (
T1.b <> T2.b
OR T1.c <> T2.c
OR T1.d <> T2.d
OR T1.e <> T2.e
)
);
ET; -- OR COMMIT depending on sytax
EXAMPLE DATA
-- BEFORE -- -- AFTER --
T1 T1
|[a]| b | c | d | e | |[a]| b | c | d | e |
|---+---+---+---+---| |---+---+---+---+---|
| 0 | 1 | 2 | 3 | 4 | | 0 | 1 | 2 | 3 | 4 |
| 1 | 2 | 3 | 4 | 5 | | 1 | 2 | 3 | 4 | 5 |
| 1 | 2 | 3 | 4 | 5 | | 1 | 2 | 3 | 4 | 5 |
| 3 | 4 | 5 | 6 | 7 | | 3 | 4 | 5 | 6 | 7 |
| 4 | 5 | 6 | 7 | 8 | | 4 | 5 | 6 | 7 | 8 |
T2 T2
|[a]| b | c | d | e | f | |[a]| b | c | d | e | f |
|---+---+---+---+---+---| |---+---+---+---+---+---|
| 0 | 1 | 2 | 3 | 4 | f | | 0 | 1 | 2 | 3 | 4 | f |
| 1 | 2 | 3 | 4 |-3 | f | | 1 | 2 | 3 | 4 |-3 | t |
| 2 | 3 | 4 | 5 | 6 | f | | 2 | 3 | 4 | 5 | 6 | f |
| 3 | 4 | 5 |-5 | 7 | f | | 3 | 4 | 5 |-5 | 7 | t |
| 4 | 5 | 6 | 7 | 8 | f | | 4 | 5 | 6 | 7 | 8 | f |
T3 T3
|[a]| b | c | d | e | |[a]| b | c | d | e |
|---+---+---+---+---| |---+---+---+---+---|
| 1 |nul|nul|nul|-3 |
| 3 |nul|nul|-5 |nul|
that solution is fine for five columns. How about 100 columns. You need dynamic T-SQL.
The below code is quite long and divided into 4 sections.
Section 1 - Create the database, test tables, and test data.
Section 2 - My user defined function for a delimited column list and Jeff Moden's split function.
Section 3 - Dynamic T-SQL, assumes first column is key and last column is flag. Uses EXCEPT command to find row differences.
Section 4 - Dynamic T-SQL, compares column 2 in T1 to column 2 in T2 with case statement. Continued for all columns. Insert into T3 for only rows that have a difference.
-- THIS CODE WILL WORK FOR A DYNAMIC LIST OF COLUMNS, NOT JUST 4! --
-- 1 - Create test tables w/data
-- the master db
use master;
go
-- create test database
create database test;
go
-- use test
use test;
go
-- create table 1
if (OBJECT_ID('t1') <> 0) drop table t1;
go
create table t1
( key1 int, col1 int, col2 int, col3 varchar(16), col4 varchar(16) );
go
-- create table 2
if (OBJECT_ID('t2') <> 0) drop table t2;
go
create table t2
( key1 int, col1 int, col2 int, col3 varchar(16), col4 varchar(16), flag1 int default 0);
go
-- create table 3
if (OBJECT_ID('t3') <> 0) drop table t3;
go
create table t3
( key1 int, col1 int, col2 int, col3 varchar(16), col4 varchar(16) );
go
-- Add 5 rows to t1
insert into t1 values (1, 2, 4, 'A', 'B');
insert into t1 values (2, 4, 8, 'C', 'D');
insert into t1 values (3, 6, 12, 'E', 'F');
insert into t1 values (4, 8, 16, 'G', 'H');
insert into t1 values (5, 10, 20, 'I', 'J');
select * from t1;
-- Add 5 rows to t2
insert into t2 (key1, col1, col2, col3, col4) values (1, 2, 4, 'A', 'B');
insert into t2 (key1, col1, col2, col3, col4) values (2, 4, 8, 'C', 'D');
insert into t2 (key1, col1, col2, col3, col4) values (3, 6, 12, 'E', 'F');
insert into t2 (key1, col1, col2, col3, col4) values (4, 8, 16, 'G', 'H');
insert into t2 (key1, col1, col2, col3, col4) values (5, 10, 20, 'I', 'K');
select * from t2;
--
-- 2A - Declare helper function for column name list
--
-- use test
use test;
go
-- remove function if it exists
if (OBJECT_ID('dbo.get_column_list') <> 0)
drop function get_column_list;
go
-- create new function
create function get_column_list (#schema_name sysname, #table_name sysname, #del_value varchar(10) = ',') returns varchar(max)
as
begin
-- nothing to do
if (#table_name is null) return null;
-- misc variables
declare #list varchar(max) = '';
-- select the changed items
select
#list += c.name + #del_value
from
sys.schemas s join sys.objects o on s.schema_id = o.schema_id
join sys.columns c on o.object_id = c.object_id
where
o.type = 'u' and
s.name = #schema_name and
o.name = #table_name
order by c.column_id;
-- remove last delimiter
select #list = substring(#list, 1, len(#list) - len(#del_value));
-- return a list
return #list
end;
go
--
-- 2B - spliter function from jeff moden
--
-- http://www.sqlservercentral.com/articles/Tally+Table/72993/
-- You download and install as TVF in [TEST] database
--
-- 3 - Find row differences using except
--
-- declare/initialize variables
declare #stmt1 varchar(max) = '';
-- column lists
declare #list1 varchar(max) = dbo.get_column_list ('dbo', 't1', ', ');
declare #list2 varchar(max) = dbo.get_column_list ('dbo', 't2', ', ');
-- key (first), flag (last)
declare #key1 sysname = left(#list1, charindex(',', #list1, 1) - 1);
declare #key2 sysname = reverse(left(reverse(#list2), charindex(',', reverse(#list2), 1) - 2));
-- make dynamic sql
select #stmt1 = '(select ' + #list1 + ' from t2 except select ' + #list1 + ' from t1) as d1';
select #stmt1 = 'update t2 set ' + #key2 + ' = 1 where ' + #key1 + ' in (select d1.' + #key1 + ' from ' + #stmt1 + ');';
-- debug line
--print #stmt1;
-- execute the sql
exec (#stmt1);
go
--
-- 4 - Find the columns differences using case
--
-- declare/initialize variables
declare #stmt varchar(max) = '';
-- column list
declare #list varchar(max) = dbo.get_column_list ('dbo', 't2', ',');
-- key (first), flag (last)
declare #key1 sysname = left(#list, charindex(',', #list, 1) - 1);
declare #key2 sysname = reverse(left(reverse(#list), charindex(',', reverse(#list), 1) - 1));
-- select the changed items (skip key & flag)
select
#stmt += 'case when s.' + Item + ' = t.' + Item + ' then null else t.' + Item + ' end as val_' + Item + ', '
from
DelimitedSplit8K (#list, ',')
where
ItemNumber not in
(
select min(ItemNumber) as skip_vals from DelimitedSplit8K (#list, ',')
union
select max(ItemNumber) as skip_vals from DelimitedSplit8K (#list, ',')
);
-- complete the statement
select #stmt = 'insert into t3 select t.' + #key1 + ',' + substring(#stmt, 1, len(#stmt) - 1) + ' from t1 as s join t2 as t on s.' + #key1 + ' = t.' + #key1 + ' where t.' + #key2 + ' = 1 ';
-- debug line
--print #stmt;
-- execute the sql
exec (#stmt);
go