Two or more results of one CASE statement in SQL - sql

Is it possible to SELECT value of two or more columns with one shot of CASE statement? I mean instead of:
select
ColumnA = case when CheckColumn='condition' then 'result1' end
,ColumnB = case when CheckColumn='condition' then 'result2' end
Something like:
select case when CheckColumn='condition' then ColumnA='result1', ColumnB='result2' end
UPDATE
Just the same as we can do with the UPDATE statement:
update CTE
set ColumnA='result1', ColumnB='result2'
where CheckColumn='condition'

It is not possible with CASE expression.
For every column you need new CASE

It is not possible, but you could use a table value constructor as a work around to this, to store each value for columna and columnb against your check column:
SELECT t.CheckColumn,
v.ColumnA,
v.ColumnB
FROM dbo.YourTable AS t
LEFT JOIN
(VALUES
('Condition1', 'Result1', 'Result2'),
('Condition2', 'Result3', 'Result4'),
('Condition3', 'Result5', 'Result6')
) AS v (CheckColumn, ColumnA, ColumnB)
ON v.CheckColumn = t.CheckColumn;
If you have more complex conditions, then you can still apply this logic, but just use a pseudo-result for the join:
SELECT t.CheckColumn,
v.ColumnA,
v.ColumnB
FROM dbo.YourTable AS t
LEFT JOIN
(VALUES
(1, 'Result1', 'Result2'),
(2, 'Result3', 'Result4'),
(3, 'Result5', 'Result6')
) AS v (ConditionID, ColumnA, ColumnB)
ON v.ConditionID = CASE WHEN <some long expression> THEN 1
WHEN <some other long expression> THEN 2
ELSE 3
END;

The equivalent select to the update is:
select 'result1', 'result2'
. . .
where CheckColumn = 'condition';
Your select is different because it produces NULL values. There is an arcane way you can essentially do this with outer apply:
select t2.*
from . . . outer apply
(select t.*
from (select 'result1' as col1, 'result2' as col2) t
where CheckColumn = 'condition'
) t2;
This will return NULL values when there is no match. And, you can have as many columns as you would like.

What I understood from your question is that you want to update multiple columns if certain condition is true.
For such situation you have to use MERGE statements.
Example of using MERGE is as given on msdn here.
Code example:
-- MERGE statement for update.
USE [Database Name];
GO
MERGE Inventory ity
USING Order ord
ON ity.ProductID = ord.ProductID
WHEN MATCHED THEN
UPDATE
SET ity.Quantity = ity.Quantity - ord.Quantity;
More MERGE statement example here.

You could solve this maybe with a CTE or a CROSS APPLY, somehting like
DECLARE #tbl2 TABLE(inx INT, val1 VARCHAR(10),val2 VARCHAR(10));
INSERT INTO #tbl2 VALUES(1,'value1a','value1b'),(2,'value2a','value2b'),(3,'value2a','value2b');
UPDATE yourTable SET col1=subTable.val1,col2=subTable.val2
FROM yourTable
CROSS APPLY(
SELECT val1,val2
FROM #tbl2
WHERE inx=1 --YourCondition
) AS subTable

Related

concatenate all columns from with names of columns also in it, one string for every row

CREATE TABLE myTable
(
COL1 int,
COL2 varchar(10),
COL3 float
)
INSERT INTO myTable
VALUES (1, 'c2r1', NULL), (2, 'c2r2', 2.335)
I want an output with for every row of a table one string with all columns and the names in it.
Something like:
COL1=1|COL2=c2r1|COL3=NULL
COL1=2|COL2=c2r2|COL3=2.3335
I have a table with lot of columns so it has to be dynamic (it would use it on different tables also), is there an easy solution where I can do it and choose separator and things like that... (It has to deal with NULL-values & numeric values also.)
I am using SQL Server 2019.
Since you are on 2019, string_agg() with a bit if JSON
Example
Select NewVal
From MyTable A
Cross Apply ( Select NewVal = string_agg([key]+'='+isnull(value,'null'),'|')
From OpenJson((Select A.* For JSON Path,Without_Array_Wrapper,INCLUDE_NULL_VALUES ))
) B
Results
NewVal
COL1=1|COL2=c2r1|COL3=null
COL1=2|COL2=c2r2|COL3=2.335000000000000e+000 -- Don't like the float
EDIT to Trap FLOATs
Select NewVal
From MyTable A
Cross Apply ( Select NewVal = string_agg([key]+'='+isnull(case when value like '%0e+0%' then concat('',convert(decimal(15,3),convert(float,value))) else value end,'null'),'|')
From OpenJson((Select A.* For JSON Path,Without_Array_Wrapper,INCLUDE_NULL_VALUES ))
) B
Results
NewVal
COL1=1|COL2=c2r1|COL3=null
COL1=2|COL2=c2r2|COL3=2.335
Would one dare to abuse json for this?
SELECT REPLACE (REPLACE (REPLACE (REPLACE (REPLACE (ca.js,'":','='), ',"','|'), '"',''), '[{','') ,'}]','') AS data
FROM (SELECT col1 as id FROM myTable) AS list
CROSS APPLY
(
SELECT t.col1
, t.col2
, cast(t.col3 as decimal(16,3)) as col3
FROM myTable t
WHERE t.col1 = list.id
FOR JSON AUTO, INCLUDE_NULL_VALUES
) ca(js)
It'll work with a simple SELECT t.* in the cross apply.
But the floats tend to be bit too long then.

UPDATE the same way as INSERT using VALUES?

I was wondering if I can use UPDATE without explicitly stating column = value. Something like this:
UPDATE table_name SET (column1, column2, column3, ...)
VALUES (value1, value2, value, ...);
The reason I need this is that I'm getting the columns and values from an array and it's dynamic for different tables with different column names and different amounts of columns, so I want to accomplish something like this in NodeJS:
BEGIN
IF NOT EXISTS (SELECT * FROM ${t_name} WHERE ${Primary_key} = ${Primary_key_value)
BEGIN
INSERT INTO ${t_name} (${cols})
VALUES (${inputs})
END
ELSE
BEGIN
UPDATE ${t_name} SET (${cols})
VALUES (${inputs})
WHERE ${Primary_key} = ${primary_key_value)
END
END
Which is essentially replacing a row that has some id with a new row having the same id.
Is there a way to accomplish something like that?
Yes, you use CROSS APPLY. I don't follow the way you are building the query, but here is the raw SQL update:
UPDATE T SET
Col1 = X.Value1
, Col2 = X.Value2
FROM MyTable T
CROSS APPLY (VALUES ('Value1','Value2')) AS X (Value2, Value2)
WHERE T.id = #Id;
You don't actually need an explicit cross apply. Just use:
UPDATE T
SET Col1 = V.Value1,
Col2 = V.Value2
FROM (VALUES ('Value1','Value2')) V(Value2, Value2)
WHERE T.id = #Id;

Conditional insert in SQL for an order system

I have a question regarding a conditional insert.
I want to be able to add the same ID in the ("tafelID" = tableID) column, ONLY if the ("betaalstatus" = pay status) is equal to true. What query do i have to use ?
I'm new to SQL. my query is below.
INSERT INTO Rekening (tafelID, betaalstatus) Select ('7', 'False')
WHERE not exists ( select 'False' )
you can replace "<...>" with the data in the nested select statement in FROM clause. you can use nested Case When statement for complex conditions.
INSERT INTO Rekening (tafelID, betaalstatus)
Select Case WHEN betaalstatus = true THEN tableID ELSE tafelID END tafelID, betaalstatus
FROM (SELECT <betaalstatus> AS betaalstatus, <tableID> AS tableID, <tafelID> AS tafelID)
-- i.e. (SELECT true AS betaalstatus, 7 AS tableID, 8 AS tafelID)

dynamically add column names and add their values as a row sql server

I have a table table1 with column name a, b, c, d, e, f.
Now the task is to get the value of each column which will definitely be a single row value and insert that into other table2 - columns(x, y, z) . So my query would be like :
insert into table2 (x, y, z)
select a, '', '' from table1
union all
select b, '', '' from table1
union all
select c, '', '' from table1
union all
select d, '', '' from table1
union all
select e, '', '' from table1
.
.
.
union all
select f, '', '' from table1
Now if a new column add in table1 then again I have to add a select statement in this. Just want to avoid this how can I write a dynamic query which automatically consider all the columns and make it shorter.
Seems like your looking for a Dynamic EAV Structure (Entity Attribute Value). Now the cool part is the #YourTable could be any query
Declare #YourTable table (ID int,Col1 varchar(25),Col2 varchar(25),Col3 varchar(25))
Insert Into #YourTable values
(1,'a','z','k')
,(2,'g','b','p')
,(3,'k','d','a')
Select A.ID
,C.*
From #YourTable A
Cross Apply (Select XMLData=cast((Select A.* for XML Raw) as xml)) B
Cross Apply (
Select Attribute = attr.value('local-name(.)','varchar(100)')
,Value = attr.value('.','varchar(max)') -- change datatype if necessary
From B.XMLData.nodes('/row') as A(r)
Cross Apply A.r.nodes('./#*') AS B(attr)
Where attr.value('local-name(.)','varchar(100)') not in ('ID','OtherFieldsToExclude') -- Field Names case sensitive
) C
Returns
ID Attribute Value
1 Col1 a
1 Col2 z
1 Col3 k
2 Col1 g
2 Col2 b
2 Col3 p
3 Col1 k
3 Col2 d
3 Col3 a
A simpler way to do this uses cross apply:
insert into table2 (x, y, z)
select v.x, '', ''
from table1 t1 cross apply
(values (t1.a), (t1.b), (t1.c), (t1.d), (t1.e), (t1.f)
) v(x);
If you want to insert new values when new columns are added to the table, then you would want a DDL and probably a DML trigger. DML triggers are the "standard" triggers.
You can read about DDL triggers in the documentation.
That said, I am highly suspicious of database systems that encourage new columns and new tables to be added. There is probably a better way to design the application, for instance, using an EAV data model that provides greater flexibility with attributes.
try this
insert into table2
select Tmp.id, tb1.* from table1 tb1,
((SELECT B.id FROM (SELECT [value] = CONVERT(XML ,'<v>' + REPLACE('a,b,c,d,e,f' , ',' , '</v><v>')+ '</v>')) A
OUTER APPLY
(SELECT id = N.v.value('.' , 'varchar(100)') FROM A.[value].nodes('/v') N ( v )) B)) Tmp
This, if I am reading it correctly, looks like a perfect time to use
PIVOT.

How to add results of a SELECT to a table

I have a SQL select statement which is comparing two tables. I am getting the values where the rows are the same. Now I have got this in the procedure I need to add these into a new table (coftReconciliationMatches). The table has all the same columns but one additional one 'MatchOrNoMatch'. I need to pass through the values of the row that are matched and also need to pass through 'Match' to the column 'MatchOrNoMatch'
This is the current part of the SQL script I have;
SELECT *
FROM dbo.coftReconciliationFileInfo AS a
WHERE EXISTS (SELECT *
FROM dbo.coftPreReconciliationInfo AS b
WHERE a.Rec_PK = b.Rec_PK
AND a.ExtRef1 = b.ExtRef1
AND a.SedolRef = b.SedolRef
AND a.ValLatest = b.ValLatest
AND a.Totunits = b.Totunits
AND a.FundsOrCash = b.FundsOrCash )
When comparing a lot of columns, the SELECT INTERSECT and SELECT EXCEPT commands can save you a lot of effort:
INSERT dbo.coftReconcilliationMatches
(Rec_PK, ExtRef1, SedolRef, ValLatest, Totunits, FundsOrCash, MatchOrNoMatch)
select Rec_PK, ExtRef1, SedolRef, ValLatest, Totunits, FundsOrCash, 'Match'
from dbo.coftReconciliationFileInfo
intersect select Rec_PK, ExtRef1, SedolRef, ValLatest, Totunits, FundsOrCash, 'Match'
from dbo.coftPreReconciliationFileInfo
(Check for typos!)
If you are creating the table on the fly (something I wouldn't recommend doing), you'd use SELECT INTO.
INSERT INTO [table] ([col1], [col2]) SELECT colx, ...
Use Select Into for this. See here http://www.w3schools.com/sql/sql_select_into.asp
Try something like:
INSERT INTO <your table> (<list of columns>)
SELECT <list of columns from select>, '<the additional column>' FROM dbo.coftReconciliationFileInfo AS a
WHERE EXISTS(SELECT * FROM dbo.coftPreReconciliationInfo AS b
WHERE a.Rec_PK = b.Rec_PK AND a.ExtRef1 = b.ExtRef1 AND a.SedolRef = b.SedolRef AND a.ValLatest = b.ValLatest AND a.Totunits = b.Totunits AND a.FundsOrCash = b.FundsOrCash )
also see http://www.1keydata.com/sql/sqlinsert.html
you need to do:
INSERT INTO Table (column1, column2..)
(SELECT column1, column2 ....
FROM dbo.coftReconciliationFileInfo AS a
WHERE EXISTS (SELECT *
FROM dbo.coftPreReconciliationInfo AS b
WHERE a.Rec_PK = b.Rec_PK
AND a.ExtRef1 = b.ExtRef1
AND a.SedolRef = b.SedolRef
AND a.ValLatest = b.ValLatest
AND a.Totunits = b.Totunits
AND a.FundsOrCash = b.FundsOrCash ))