how to include the column name in case statement - sql

I need to include a column name in the where clause of SQL query depending on a variable value.
For Example,
1. If I have a variable,say,#test and I assigned the variable with the value 1/0.
2. I have a table ,say, table1 with 2 column names col1,col2.
Now, I am writing a query to fetch some data from the table by including only one column at a time depends on the #test variable(declared earlier) i.e. I need to include col1 in the where clause of the query if the #test is 1 and col2 if #test is 0.
Please let me know the solution asap.
Thanks in advance

Select *
From dbo.MyTable
Where Case When #Test = 1 Then Col1 Else Col2 End > 100

declare #tbl table
(
col1 int,
col2 int
)
insert into #tbl select 10, 20
declare #test int
set #test = 1
select *
from #tbl
Where Case When #test = 1 Then Col1 Else Col2 End = 10
If datatype is different
select *
from #tbl
Where Case when #test = 1 Then Col1 end = 10
OR
Case when #test = 2 Then Col2 end = 'sometext'

A slightly different approach:
Select *
From dbo.MyTable
Where Col1 = Case When #Test = 1 Then 10 Else Col1 End and
Col2 = Case When #Test = 0 Then 'sometext' Else Col2 End
This version of the query may be sargable.

Related

SQL statement with CASE WHEN in ORDER BY causes type convert error

Executing the following code triggers an error:
System.Data.SqlClient.SqlException: 'Cannot convert a char value to money. The char value has incorrect syntax.
All was fine until I have added the second parameter used for sorting purposes.
The code is simplification for clarity.
query as String = "
SELECT a, b, c
FROM DataTable
WHERE c = #PARAM
ORDER BY
CASE #SORTCOLUMN
WHEN 1 THEN a
WHEN 2 THEN b
WHEN 3 THEN c
END"
Dim param As String = "myprarameter"
Dim sortcolumn as Integer = 1
result = connection.Query(Of MyType)(query, New With {Key .PARAM = param, Key .SORTCOLUMN = sortcolumn})
UPDATE:
After hours spent on testing I have narrowed it down to purely SQL issue, not Dapper nor .NET Framework.
Here are my findings:
All works fine until all columns used within CASE WHEN are of the same type (or types and even values which can be easily converted into each other). Once types of columns are different and values cannot be converted, it returns type conversion error. Seems it is trying to convert type of the column selected by CASE WHEN to the type of the first WHENcolumn.
Herewith two examples. I have removed variables for simplicity.
This works:
CREATE TABLE #TestTable1
(col1 money, col2 money)
INSERT INTO #TestTable1
(col1, col2)
VALUES
(1, 30),
(2, 20),
(3, 10)
SELECT col1, col2
FROM #TestTable1
ORDER BY
CASE 2
WHEN 1 THEN col1
WHEN 2 THEN col2
END
DROP TABLE #TestTable1
But this does NOT work. Returns:
Cannot convert a char value to money. The char value has incorrect syntax.
CREATE TABLE #TestTable2
(col1 money, col2 varchar(20))
INSERT INTO #TestTable2
(col1, col2)
VALUES
(1, 'cc'),
(2, 'bb'),
(3, 'aa')
SELECT col1, col2
FROM #TestTable2
ORDER BY
CASE 2
WHEN 1 THEN col1
WHEN 2 THEN col2
END
DROP TABLE #TestTable2
I am using Azure SQL with compatibility level 150.
I have updated Title and Tags accordingly.
UPDATE 2:
I am trying to add a complication to #forpas solution in form of a second parameter which will tell the order. I have used CASE within CASE but this returns a number of syntax errors.
The ORDER part only. The rest has not changed.
ORDER BY
CASE #SORTORDER
WHEN 'a' THEN
(CASE #SORTCOLUMN
WHEN 1 THEN col1
WHEN 2 THEN col2
END,
CASE #SORTCOLUMN
WHEN 3 THEN col3
WHEN 4 THEN col4
END) ASC
WHEN 'd' THEN
(CASE #SORTCOLUMN
WHEN 1 THEN col1
WHEN 2 THEN col2
END,
CASE #SORTCOLUMN
WHEN 3 THEN col3
WHEN 4 THEN col4
END) DESC
END
One solution is to split the CASE statement to as many CASE statements needed so each one contains columns with the same or similar convertable data types:
CREATE TABLE #TestTable2
(col1 money, col2 decimal(18, 2), col3 varchar(20))
INSERT INTO #TestTable2
(col1, col2, col3)
VALUES
(1, 5.5, 'cc'),
(2, 1.8, 'bb'),
(3, 3.3, 'aa');
DECLARE #SORTCOLUMN INT = 1; -- 1 or 2 or 3
SELECT col1, col2, col3
FROM #TestTable2
ORDER BY
CASE #SORTCOLUMN
WHEN 1 THEN col1
WHEN 2 THEN col2
END,
CASE #SORTCOLUMN
WHEN 3 THEN col3
END
DROP TABLE #TestTable2
See the demo.
Simply create the SQL as nvarchar and execute it as follows:
DECLARE #SQL NVARCHAR(MAX) = N'SELECT a, b, c FROM DataTable
WHERE c = #PARAM
ORDER BY ' + (CASE #SORTCOLUMN WHEN 1 THEN 'a' WHEN 2 THEN 'b' WHEN 3 THEN 'c' ELSE 'a' END);
EXEC sp_Executesql #SQL;
in VB
query as String = "DECLARE #SQL NVARCHAR(MAX) = N'SELECT a, b, c FROM DataTable
WHERE c = #PARAM
ORDER BY ' + (CASE #SORTCOLUMN WHEN 1 THEN 'a' WHEN 2 THEN 'b' WHEN 3 THEN 'c' ELSE 'a' END);
EXEC sp_Executesql #SQL;"
try this.
query as String = "
SELECT a, b, c
FROM DataTable
WHERE c = #PARAM
ORDER BY
CASE #SORTCOLUMN
WHEN '1' THEN a
WHEN '2' THEN b
WHEN '3' THEN c
END"

Subtract two columns(Col1-Col2) and add remaining to next row in Col1

I'm trying to work on a logic which I cannot seem to figure out a way to do it.
Problem:
I have three columns PrimaryKey, COl1 and COl2 as shown in below screenshot
Let's take a new column Col3 = Col1-Col2,
I am adding the remaining in Col3 to Col1 of next row and again subtract it to get Col3.
Let us consider the table above and for
PrimaryKey=1 --> Col3 = 10.2 - 5 = 5.2.
This 5.2 must be added to Col1 of PrimaryKey=2 which is
15 + 5.2 = 20.2.
Now again Col3 = 20.2 - 3 = 17.2, like this it has to iterate for next records.
I hope that I am clear enough in explaining my issue. Please let me know if you need any further explanation.
*The table provided is just a sample table, The actual table that I am working is very large.
Thank you.
As far as I can tell, you want the cumulative value of col2 subtracted from the cumulative of col1. In SQL Server 2012+, you would do:
select t.*,
sum(col2 - col1) over (order by primary key)
from t;
You can use a self-join on the original table, but there's a danger if the primary key isn't complete, such as from deleting records at some point. Here I use a self-join with ROW_NUMBER to create a definitely sequential key - this allows me to join [this row number] to [this row number + 1] (sample data included in case anyone else has a better method):
DECLARE #YourTable TABLE (PrimaryKey INT IDENTITY(1,1), COL1 FLOAT, COL2 FLOAT)
INSERT INTO #YourTable
( COL1, COL2 )
VALUES
(10.2, 5.0)
, (15.0, 3.0)
, (5.7, 6)
, (9.0, 5.5)
; WITH TableRanked
AS (
SELECT PrimaryKey ,
COL1 ,
COL2
, COL3 = COL1 - COL2
, RowNum = ROW_NUMBER() OVER(ORDER BY PrimaryKey)
FROM #YourTable
)
SELECT tr.PrimaryKey ,
tr.COL1 ,
tr.COL2 ,
tr.COL3
, COL1Next = COALESCE(trNext.COL1, 0)
, COL4 = tr.COL3 + COALESCE(trNext.COL1, 0)
FROM TableRanked tr
LEFT JOIN TableRanked trNext
-- Here's where the magic happens:
ON trNext.RowNum = (tr.RowNum + 1)
If you are using SQL Server 2012 you can use LEAD. This will get you the same results as what Russel posted but with better performance:
DECLARE #YourTable TABLE (PrimaryKey INT IDENTITY(1,1), COL1 FLOAT, COL2 FLOAT)
INSERT INTO #YourTable (COL1, COL2 )
VALUES (10.2, 5.0), (15.0, 3.0), (5.7, 6), (9.0, 5.5);
SELECT *, (COL1 - COL2) + LEAD(COL1,1,0) OVER (ORDER BY PrimaryKey)
FROM #YourTable;
Let me know if this query helps:
DECLARE #n int
SELECT #n = count(*) from #yourTable
DECLARE #counter int
SELECT #counter = MIN(PrimaryKeyCol) from #yourTable
DECLARE #col3 = 0
WHILE (#counter <= #n)
SELECT #col3 = #col3 + Col1 - Col2 from #yourTable where PrimaryKeyCol = #counter
Print #col3
#counter = #counter + 1
END

Stored procedure setting a value if row is empty

How would I go about setting a value if a row is empty ('')?
I was thinking something like,
Got var with default value called #defaultValue to set it where the row in a table is ''.
if (select col1 from table1 where col1 = '')
set (select col1 from table1 where col1 = '') = #DefaultValue
is there a better way?
code is just a draft its not even tested..
If you want to update the table with #DefaultValue, you can use WHERE clause in the UPDATE query:
UPDATE table1
SET col1=#DefaultValue
WHERE col1=''
OR col1 IS NULL
OR
If you are trying to select #DefaultValue if the column is empty or null, you can do this:
SELECT CASE WHEN (col1 IS NULL OR col1='')
THEN #DefaultValue
ELSE col1
END AS Col1
FROM table1
select case when col1 ='' then #DefaultValues else col1 end from table
DEMO
declare #default int
set #default=1
declare #tbl table(col1 int)
insert into #tbl values(1),(''),(2)
select case when col1='' or col1 is null then #default else col1 end from #tbl

Statement blocks in SQL SELECT using IF ELSE

I'm trying to return different data depending on a variable in a SELECT. Something like this:
SELECT
IF #variable = 'YES'
column1, column2
ELSE
column3
FROM TABLE
What is this the proper way to use the IF condition in SQL? Or is there a better alternative to what I'm trying to accomplish?
If you want to return a different number of columns, you'll need to use an IF:
IF #variable = 'YES'
BEGIN
SELECT column1, column2
FROM YourTable
END
ELSE
BEGIN
SELECT column3
FROM YourTable
END
If you want different data on the same column (assuming the same datatype), you could use a CASE:
SELECT CASE WHEN #variable = 'YES' THEN column1 ELSE Column2 AS Data
FROM YourTable
You can use an IF statement, but you'll need to set up multiple queries. You can use a CASE for selecting one column or another, but not to select one or multiple like in your question.
DECLARE #var INT = 1;
DECLARE #test TABLE (
Col1 int,
Col2 int,
Col3 int
)
INSERT INTO #test VALUES (1,2,3)
IF #var = 1
BEGIN
SELECT Col1, Col2
FROM #test
END
ELSE
BEGIN
SELECT Col3
FROM #test
END

Tidiest way to filter out rows where all columns = a value

I have a query with loads of columns. I want to select rows where not all the columns are equal to 0.
select * from table
where
not
( column1 = 0 and
column2 = 0 and
column3 = 0 and
...
column45 = 0)
Is this really the tidiest way to do it?
Supposing I then need to change it to ignore when all columns are 1, or negative.. Its a lot of cut and paste..
It appears as though the 45 individual columns have a similar meaning. As such, I would encourage you to properly normalize this table. If you did, the query would be simpler and would likely perform better.
You could parameterize the query and put it in a stored procedure or table-valued function. You'd only need to write the query a fixed number of times (once per operation type) regardless of the value(s) you choose.
create function dbo.fn_notequal_columns
(
#value int
)
returns table
as
(
select * from [table]
where column1 <> #value and column2 <> #value ...
)
select * from dbo.fn_notequal_columns(0)
You could use CHECKSUM. However, I don't know the internals of CHECKSUM so can't guarantee it would work over large datasets.
CREATE TABLE dbo.FooBar (
keyCol int NOT NULL IDENTITY (1, 1),
col1 int NOT NULL,
col2 int NOT NULL,
col3 int NOT NULL
)
INSERT FooBar (col1, col2, col3)
SELECT -45, 0, 45
UNION ALL
SELECT 0, 23, 0
UNION ALL
SELECT 0, 0, 0
UNION ALL
SELECT 1, 0, 0
SELECT
CHECKSUM(col1, col2, col3)
FROM
dbo.FooBar
SELECT
*
FROM
dbo.FooBar
WHERE
CHECKSUM(col1, col2, col3) = 0
(1) You have the wrong connective in the condition - you need OR and not AND.
With the question amended, the observation above is no longer correct.
(2) If you have 45 columns that you need to filter on, you are going to be hard pressed to do any better than what you have written. Pain though it be...
This observation remains true.
You could add a computed column that does the calculation for you. It is not technically any tidier, except that now when you use it in any query you only have to check the computed column as opposed to repeating the calculation.
CREATE TABLE dbo.foo
(
col1 INT,
col2 INT,
col3 INT,
all_0 AS
(
CONVERT(BIT, CASE
WHEN col1 = 0 AND col2 = 0 AND col3 = 0
THEN 1 ELSE 0
END)
)
);
If your numbers are constrained in some way to be >= 0, you could do something slightly tidier, such as:
WHERE col1 + col2 + col3 = 0 -- or 45, if there are 45 such columns
-- and you are looking for each column = 1
you could create a view of a normalized structure and use that as your source for this query:
SELECT all other fields, 'Column1', COL1 FROM tableName
UNION
SELECT all other fields, 'Column2, COL2 FROM TableName
UNION ...
SELECT all other fields, 'Column45', COL45 FROM tableName