How to run query for this - tsql

MyTable has four columns.
Condition1 | Condition2 | CondEquation | EquationResult
---------------------------------------------------------
1 | 0 | C1 & C2 | 0
1 | 1 | C1 & C2 | 1
EquationResult data is select C1 & C2. It is evaluated expression of CondEquation.
How to update the 4th column using SQL statements.
Is there anyway I can write function for this?
SQL Server 2008 R2
Thank you,
Smith

Sure. But I can only deliver you a solution that is based on a cursor, I hope that's not a problem.
use testing
-- create table test_01 (c1 int, c2 int, ce nvarchar(100), result int)
insert into test_01 (c1, c2, ce) values (1, 0, 'c1 & c2')
insert into test_01 (c1, c2, ce) values (1, 1, 'c1 & c2')
insert into test_01 (c1, c2, ce) values (7, 3, 'c1 & c2')
insert into test_01 (c1, c2, ce) values (2, 4, 'c1 | c2')
declare #eq nvarchar(100)
declare #sql_statement nvarchar(500)
declare c cursor for select ce from test_01
open c
fetch next from c into #eq
while ##fetch_status = 0
begin
-- update test_01 set result = (c1 & c2) where current of c
set #sql_statement = 'update test_01 set result = (' + #eq + ') where current of c'
exec sp_executesql #sql_statement
fetch next from c into #eq
end
close c
deallocate c
select * from test_01
This gives the following results:
c1 c2 ce result
1 0 c1 & c2 0
1 1 c1 & c2 1
7 3 c1 & c2 3
2 4 c1 | c2 6

Here is a script that will show the cEquationResult even when data change in the table, it can only handle the bit operators & and |
Table to represent your table:
create table t_test(condition1 bit, condition2 bit, condition3 bit, CondEquation varchar(20))
insert t_test values(1,0, 0, 'c1&c2|c3')
insert t_test values(1,1, 1, 'c1&c2 | c3')
go
Function to calculate the calculated bit. Yes it IS a mean one:
create function f_t(#condition1 bit, #condition2 bit, #condition3 bit, #CondEquation varchar(10))
returns bit
as
begin
declare #result bit
;with a as
(
select replace(replace(replace(replace(#CondEquation, 'c1',#condition1), 'c2',#condition2), 'c3',#condition3), ' ','') n
),
b as
(
select n, 1 rn from a
union all
select stuff(n, patindex('%&%', n) - 1, 3 , case when substring(n, patindex('%&%', n) - 1, 3) like '%0%' then 0 else 1 end), rn+1
from b
where patindex('%&%', n)> 0
), c as
(
select n from (
select n, row_number() over (order by rn desc) rn2 from b
) a where rn2 = 1
), d as
(
select n, 1 rn from c
union all
select stuff(n, patindex('%|%', n) - 1, 3 , case when substring(n, patindex('%|%', n) - 1, 3) like '%1%' then 1 else 0 end), rn+1
from d
where patindex('%|%', n)> 0
), e as
(
select n from (
select n, row_number() over (order by rn desc) rn2 from d
) a where rn2 = 1
)
select #result=n from e
return #result
end
go
adding the extra field to show the calculated bit
ALTER TABLE t_test ADD cEquationResult AS
dbo.f_t(condition1, condition2, condition3, CondEquation)
Testing the scripts:
select * from t_test

Related

Multiple case in update postgres

I need to update 2 columns in table with same conditions. I know, that each of them would take a lot of time. How can I concatenate 2 updates into 1, which can be faster?
-- first update
update t1
set col1 =
case when cc1 is not NULL and cc1 <> 0 then 'A'
when cc2 is not NULL and cc2 <> 0 then 'B'
when cc3 is not NULL and cc3 <> 0 then 'C'
else null
end;
-- with same cond
update t1
set col2 =
case when cc1 is not NULL and cc1 <> 0 then 'qwe rty'
when cc2 is not NULL and cc2 <> 0 then 'qzaz wsx'
when cc3 is not NULL and cc3 <> 0 then 'zxcv asdf'
else 'pl ok'
end;
-- my effort to concatenate, dont work
update t1
set (col1, col2) =
(select c1, c2 from
(select case when t2.cc1 is not NULL and t2.cc1 <> 0 then 'A' as c1, 'qwe rty' as c2
when t2.cc2 is not NULL and t2.cc2 <> 0 then ('B', 'qaz wsx')
when t2.cc3 is not NULL and t2.cc3 <> 0 then ('C', ' zxcv asdf')
else (null, 'pl ok')
end
from t1 as t2 where t1.key_column1 = t2.key_column1 and t1.key_column2 = t2.key_column2 and t1.key_column3 = t2.key_column3) f)
;
This is the way I would do it.
WITH cte AS (SELECT * FROM
(VALUES(1, 'A', 'qwe rty'),(2, 'B', 'qaz wsx'),(3, 'C', 'zxcv asdf'),(4, NULL, 'pl ok')) v (id,c1,c2))
UPDATE so_demo
SET col1 = cte.c1, col2 = cte.c2
FROM cte WHERE cte.id = CASE WHEN COALESCE(cc1, 0) <> 0 THEN 1
WHEN COALESCE(cc2, 0) <> 0 THEN 2
WHEN COALESCE(cc3, 0) <> 0 THEN 3
ELSE 4 END;
By way of explanation, I have put the possible values into a cte assigning them an id in addition to the values. I can then put the case statement in the where clause generating the necessary id. Note the use of COALESCE to make the WHENs simpler to read.
One way is to use arrays.
UPDATE t1
SET (col1,
col2) = (SELECT x[1],
x[2]
FROM (SELECT CASE
WHEN cc1 IS NOT NULL
AND cc1 <> 0 THEN
ARRAY['A',
'qwe rty']
WHEN cc2 IS NOT NULL
AND cc2 <> 0 THEN
ARRAY['B',
'qzaz wsx']
...
ELSE
ARRAY[NULL,
'pl ok']
END) AS x
(x));
But in terms of runtime optimization the gain compared to just UPDATE ... SET col1 = CASE ..., col2 = CASE ... should be neglectable, if any.

how to calculate a string statement

Given a string combination which is a calculation statement, how can I get the result, in this case is column cal in below code.
I know I can use case but is there any direct way to do the calculation?
create table tl_test
(
cl1 int
)
create table tl_test2
(
cl1 char(1)
)
insert into tl_test values (21), (43), (13), (36), (41)
insert into tl_test2 values ( '+'), ('-'), ('*'), ('/')
select *,
cast(c1 as varchar) + f1
+ cast(c2 as varchar) + f2
+ cast(c3 as varchar) + f3
+ cast(c4 as varchar) + f4
+ cast(c5 as varchar) as cal
from(
SELECT A.cl1 as c1, f1.cl1 as f1, b.cl1 as c2,f2.cl1 as f2, C.cl1 as c3, f3.cl1 as f3, D.cl1 as c4, f4.cl1 as f4, E.cl1 as c5
FROM TL_TEST A
CROSS JOIN TL_TEST2 f1
CROSS JOIN TL_TEST B
CROSS JOIN TL_TEST2 f2
CROSS JOIN TL_TEST C
CROSS JOIN TL_TEST2 f3
CROSS JOIN TL_TEST D
CROSS JOIN TL_TEST2 f4
CROSS JOIN TL_TEST E
)a
WHERE c1 != c2
and c1 != c3
and c1 != c4
and c1 != c5
and c2 != c3
and c2 != c4
and c2 != c5
and c3 != c4
and c3 != c5
and c4 != c5
You could store the result of the cal column in a string and use EXEC to calculate your answer.
DECLARE #sql NVARCHAR(MAX) = 'SELECT ' + (SELECT TOP 1 cal FROM result)
EXEC(#sql)
Read this related thread, especially the answer by Erland Sommarskog.
I'm sorry, there is no way to do this in pure ad-hoc T-SQL.
Possible workarounds:
1) You can use dynamic SQL, which has one flaw: Your example would be calculated for INTs
DECLARE #cmd VARCHAR(100)='SELECT (' + '21*41-36 / 13+43' /*Your formula coming from somewhere*/ + ')';
EXEC(#cmd);
Results 902
2) You can use XMLs implicit ability to calculate like this
SELECT CAST('' AS XML).value('21*41-36 div 13+43','float')
hint: "/" must be replaced with " div "
Results 901.23077
It's a pitty, that the XML type's .value() knows expressions and values. A value can be introduced from a table's column dynamically (sql:column("ColumnName")), but an expression must be a literal.
3) And you might include .Net-code as assembly (CLR function).
Conclusio
Sorry, there is no easy going...
If you need exact results you should use the XML approach in dynamically created SQL.
UPDATE Working example
DECLARE #tbl TABLE(ID INT IDENTITY,SomeFormula VARCHAR(100));
INSERT INTO #tbl(SomeFormula) VALUES
('1+2')
,('21*41-36/13+43')
,('(1+3)*4')
,('3.6 div (2.5-1)');
DECLARE #cmd VARCHAR(MAX)=
(
SELECT 'DECLARE #x XML=CAST('''' AS XML);' +
STUFF
(
(
SELECT ' UNION ALL SELECT ' + CAST(ID AS VARCHAR(MAX)) + ' AS ID, #x.value(''' + REPLACE(SomeFormula,'/',' div ') + ''',''float'')'
FROM #tbl
FOR XML PATH('')
),1,11,''
)
);
PRINT #cmd;
EXEC (#cmd);
This is the generated statement
DECLARE #x XML=CAST('' AS XML);
SELECT 1 AS ID, #x.value('1+2','float')
UNION ALL SELECT 2 AS ID, #x.value('21*41-36 div 13+43','float')
UNION ALL SELECT 3 AS ID, #x.value('(1+3)*4','float')
UNION ALL SELECT 4 AS ID, #x.value('3.6 div (2.5-1)','float')
And this is the result
ID
1 3
2 901,23077
3 16
4 2,4

How to compare two identicals tables data of each column in postgres?

I want compare two table's all column values.The two table is identical tables means column number is same and primary key is same. can any one suggest query which compare such two tables in postgres.
The query should give the column name and what is the two different value of two tables.Like this
pkey | column_name | table1_value | table2_value
123 | bonus | 1 | 0
To get all different rows you can use:
select *
from table_1 t1
join table_2 t2 on t1.pkey = t2.pkey
where t1 is distinct from t2;
This will only compare rows that exist in both tables. If you also want to find those that are missing in on of them use a full outer join:
select coalesce(t1.pkey, t2.pkey) as pkey,
case
when t1.pkey is null then 'Missing in table_1'
when t2.pkey is null then 'Missing in table_2'
else 'At least one column is different'
end as status,
*
from table_1 t1
full ojoin table_2 t2 on t1.pkey = t2.pkey
where (t1 is distinct from t2)
or (t1.pkey is null)
or (t2.pkey is null);
If you install the hstore extension, you can view the differences as a key/value map:
select coalesce(t1.pkey, t2.pkey) as pkey,
case
when t1.pkey is null then 'Missing in table_1'
when t2.pkey is null then 'Missing in table_2'
else 'At least one column is different'
end as status,
hstore(t1) - hstore(t2) as values_in_table_1,
hstore(t2) - hstore(t1) as values_in_table_2
from table_1 t1
full ojoin table_2 t2 on t1.pkey = t2.pkey
where (t1 is distinct from t2)
or (t1.pkey is null)
or (t2.pkey is null);
Using this sample data:
create table table_1 (pkey integer primary key, col_1 text, col_2 int);
insert into table_1 (pkey, col_1, col_2)
values (1, 'a', 1), (2, 'b', 2), (3, 'c', 3), (5, 'e', 42);
create table table_2 (pkey integer primary key, col_1 text, col_2 int);
insert into table_2 (pkey, col_1, col_2)
values (1,'a', 1), (2, 'x', 2), (3, 'c', 33), (4, 'd', 52);
A possible result would be:
pkey | status | values_in_table_1 | values_in_table_2
-----+----------------------------------+-------------------+------------------
2 | At least one column is different | "col_1"=>"b" | "col_1"=>"x"
3 | At least one column is different | "col_2"=>"3" | "col_2"=>"33"
4 | Missing in table_1 | |
5 | Missing in table_2 | |
Example data:
create table test1(pkey serial primary key, str text, val int);
insert into test1 (str, val) values ('a', 1), ('b', 2), ('c', 3);
create table test2(pkey serial primary key, str text, val int);
insert into test2 (str, val) values ('a', 1), ('x', 2), ('c', 33);
This simple query gives a complete information on differences of two tables (including rows missing in one of them):
(select 1 t, * from test1
except
select 1 t, * from test2)
union all
(select 2 t, * from test2
except
select 2 t, * from test1)
order by pkey, t;
t | pkey | str | val
---+------+-----+-----
1 | 2 | b | 2
2 | 2 | x | 2
1 | 3 | c | 3
2 | 3 | c | 33
(4 rows)
In Postgres 9.5+ you can transpose the result to the expected format using jsonb functions:
select pkey, key as column, val[1] as value_1, val[2] as value_2
from (
select pkey, key, array_agg(value order by t) val
from (
select t, pkey, key, value
from (
(select 1 t, * from test1
except
select 1 t, * from test2)
union all
(select 2 t, * from test2
except
select 2 t, * from test1)
) s,
lateral jsonb_each_text(to_jsonb(s))
group by 1, 2, 3, 4
) s
group by 1, 2
) s
where key <> 't' and val[1] <> val[2]
order by pkey;
pkey | column | value_1 | value_2
------+--------+---------+---------
2 | str | b | x
3 | val | 3 | 33
(2 rows)
I tried all of the above answer.Thanks guys for your help.Bot after googling I found a simple query.
SELECT <common_column_list> from table1
EXCEPT
SELECT <common_column_list> from table2.
It shows all the row of table1 if any table1 column value is different from table2 column value.
Not very nice but fun and it works :o)
Just replace public.mytable1 and public.mytable2 by correct tables and
update the " where table_schema='public' and table_name='mytable1'"
select * from (
select pkey,column_name,t1.col_value table1_value,t2.col_value table2_value from (
select pkey,generate_subscripts(t,1) ordinal_position,unnest(t) col_value from (
select pkey,
(
replace(regexp_replace( -- null fields
'{'||substring(a::character varying,'^.(.*).$') ||'}' -- {} instead of ()
,'([\{,])([,\}])','\1null\2','g'),',,',',null,')
)::TEXT[] t
from public.mytable1 a
) a) t1
left join (
select pkey,generate_subscripts(t,1) ordinal_position,unnest(t) col_value from (
select pkey,
(
replace(regexp_replace( -- null fields
'{'||substring(a::character varying,'^.(.*).$') ||'}' -- {} instead of ()
,'([\{,])([,\}])','\1null\2','g'),',,',',null,')
)::TEXT[] t
from public.mytable2 a
) a) t2 using (pkey,ordinal_position)
join (select * from information_schema.columns where table_schema='public' and table_name='mytable1') c using (ordinal_position)
) final where COALESCE(table1_value,'')!=COALESCE(table2_value,'')

Using CTE instead of Cursor

I have the following table structure.
I just want to update SubId to all the rows where it is null and where the RawLineNumber is ascending by 1 and also the SeqNumber ascending by 1.
RawlineNumber Claimid SubId SeqNumber
1 6000 A100 1
2 6000 NULL 2
3 6000 NULL 3
10 6000 A200 1
11 6000 NULL 2
25 6000 A300 1
26 6000 NULL 2
27 6000 NULL 3
I want to update
SubId of RawLineNumber 2 and 3 with A100,
SubId of RawLineNumber 11 with A200,
SubId of RawLineNumber 26 and 27 with A300.
I have a cursor which does the job but can I have a CTE to take care of it ?
UPDATE m
SET subid = q.subid
FROM mytable m
CROSS APPLY
(
SELECT TOP 1 subid
FROM mytable mi
WHERE mi.rawLineNumber < m.rawLineNumber
AND mi.subid IS NOT NULL
ORDER BY
rawLineNumber DESC
) q
WHERE m.subid IS NULL
Since a recusive solution was requested, I decided to write one. Also it works for gaps in Seqnumbers and RawlineNumber
declare #t table (RawlineNumber int, Claimid int, SubId varchar(5), SeqNumber int)
insert #t values(1, 6000, 'A100', 1)
insert #t values(2, 6000, NULL, 2)
insert #t values(3, 6000, NULL, 3)
insert #t values(10, 6000, 'A200', 1)
insert #t values(11, 6000, NULL, 2)
insert #t values(25, 6000, 'A300', 1)
insert #t values(26, 6000, NULL, 2)
insert #t values(27, 6000, NULL, 3)
;with cte as
(
select Rawlinenumber, SeqNumber, SubId
from #t where SubId is not null and SeqNumber = 1
union all
select t.Rawlinenumber, t.SeqNumber, c.SubId
from cte c
join
#t t
on c.Rawlinenumber + 1 = t.Rawlinenumber
and c.SeqNumber + 1 = t.SeqNumber
where t.SubId is null and t.SeqNumber > 1
)
update t
set SubId = c.SubId
from #t t join cte c
on c.Rawlinenumber = t.Rawlinenumber
where t.SeqNumber > 1
select * from #t
A not-so simple SQL script should achieve what you want:
update my_table t1 set t1.subid =
(select t2.subid from my_table t2
where t2.rawlinenumber < t1.rawlinenumber
and t2.seqnumber = 1
and t2.rawlinenumber = (
select max(t3.rawlinenumber)
from my_table t3
where t3.seq_number = 1
and t3.rawlinenumber <= t2.rawlinenumber)
where t1.subid is null;
The inner subselect (T3) gives us the last row having seqnumber = 1 before the current line,
the outer subselect gives us the SubID for this row (using windowing functions would be more efficient, but since you didn't mention a specific RDBMS, I stick with this :-) )

T-SQL -- convert comma-delimited column into multiple columns

From the table below, how can I convert the Values column into multiple columns, populated with individual values that are currently separated by commas? Before the conversion:
Name Values
---- ------
John val,val2,val3
Peter val5,val7,val9,val14
Lesli val8,val34,val36,val65,val71,val
Amy val3,val5,val99
The result of the conversion should look like:
Name Col1 Col2 Col3 Col4 Col5 Col6
---- ---- ---- ---- ---- ---- ----
John val val2 val3
Peter val5 val7 val9 val14
Lesli val8 val34 val36 val65 val71 val
Amy val3 val5 val99
First, what database product and version are you using? If you are using SQL Server 2005 and later, you can write a Split user-defined function like so:
CREATE FUNCTION [dbo].[Split]
(
#DelimitedList nvarchar(max)
, #Delimiter varchar(2) = ','
)
RETURNS TABLE
AS
RETURN
(
With CorrectedList As
(
Select Case When Left(#DelimitedList, DataLength(#Delimiter)) <> #Delimiter Then #Delimiter Else '' End
+ #DelimitedList
+ Case When Right(#DelimitedList, DataLength(#Delimiter)) <> #Delimiter Then #Delimiter Else '' End
As List
, DataLength(#Delimiter) As DelimiterLen
)
, Numbers As
(
Select TOP (Coalesce(Len(#DelimitedList),1)) Row_Number() Over ( Order By c1.object_id ) As Value
From sys.objects As c1
Cross Join sys.columns As c2
)
Select CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen As Position
, Substring (
CL.List
, CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen
, CharIndex(#Delimiter, CL.list, N.Value + 1)
- ( CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen )
) As Value
From CorrectedList As CL
Cross Join Numbers As N
Where N.Value < Len(CL.List)
And Substring(CL.List, N.Value, CL.DelimiterLen) = #Delimiter
)
You can then split out the values in you want using something akin to:
Select Name, Values
From Table1 As T1
Where Exists (
Select 1
From Table2 As T2
Cross Apply dbo.Split (T1.Values, ',') As T1Values
Cross Apply dbo.Split (T2.Values, ',') As T2Values
Where T2.Values.Value = T1Values.Value
And T1.Name = T2.Name
)
Here is a solution that uses a recursive cte to generate a "table of numbers" (courtesy of Itzik Ben-Gan), which is useful for all manner of problems including string splitting, and PIVOT. SQL Server 2005 onwards. Full table create, insert and select script included.
CREATE TABLE dbo.Table1
(
Name VARCHAR(30),
[Values] VARCHAR(128)
)
GO
INSERT INTO dbo.Table1 VALUES ('John', 'val,val2,val3')
INSERT INTO dbo.Table1 VALUES ('Peter', 'val5,val7,val9,val14')
INSERT INTO dbo.Table1 VALUES ('Lesli', 'val8,val34,val36,val65,val71,val')
INSERT INTO dbo.Table1 VALUES ('Amy', 'val3,val5,val99')
GO
SELECT * FROM dbo.Table1;
GO
WITH
L0 AS(SELECT 1 AS c UNION ALL SELECT 1),
L1 AS(SELECT 1 AS c FROM L0 AS A, L0 AS B),
L2 AS(SELECT 1 AS c FROM L1 AS A, L1 AS B),
L3 AS(SELECT 1 AS c FROM L2 AS A, L2 AS B),
Numbers AS(SELECT ROW_NUMBER() OVER(ORDER BY c) AS n FROM L3)
SELECT Name, [1] AS Column1, [2] AS Column2, [3] AS Column3, [4] AS Column4, [5] AS Column5, [6] AS Column6, [7] AS Column7
FROM
(SELECT Name,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY nums.n) AS PositionInList,
LTRIM(RTRIM(SUBSTRING(valueTable.[Values], nums.n, charindex(N',', valueTable.[Values] + N',', nums.n) - nums.n))) AS [Value]
FROM Numbers AS nums INNER JOIN dbo.Table1 AS valueTable ON nums.n <= CONVERT(int, LEN(valueTable.[Values])) AND SUBSTRING(N',' + valueTable.[Values], n, 1) = N',') AS SourceTable
PIVOT
(
MAX([VALUE]) FOR PositionInList IN ([1], [2], [3], [4], [5], [6], [7])
) AS Table2
GO
--DROP TABLE dbo.Table1
Which converts this output
Name Values
John val,val2,val3
Peter val5,val7,val9,val14
Lesli val8,val34,val36,val65,val71,val
Amy val3,val5,val99
to
Name Column1 Column2 Column3 Column4 Column5 Column6 Column7
Amy val3 val5 val99 NULL NULL NULL NULL
John val val2 val3 NULL NULL NULL NULL
Lesli val8 val34 val36 val65 val71 val NULL
Peter val5 val7 val9 val14 NULL NULL NULL