Multiplying two columns which have been calculated on a CASE statement - sql

I am performing a SQL query in PostgreSQL using a CASE statement like this:
SELECT
CASE column1
WHEN something THEN 10
ELSE 20
END AS newcol1
CASE column12
WHEN something THEN 30
ELSE 40
END AS newcol2
COUNT(column3) newcol3
FROM table
GROUP BY newcol1,newcol2,newcol3
I need a fourth column which has to be the result of newcol2 * newcol3, how can I do that?
If I put (newcol2 * newcol3) AS newcol4 I get a syntax error.

You can always use a CTE to abstract things away to a different level, if that helps - something along the lines of ...
With CTE as
(
SELECT
CASE column1
WHEN something THEN 10
ELSE 20
END AS newcol1,
CASE column12
WHEN something THEN 30
ELSE 40
END AS newcol2,
column3,
FROM table
)
SELECT
newcol1, newcol2,
count(column3) as newcol3,
(newcol2 * newcol3) AS newcol4
FROM CTE
GROUP BY newcol1,newcol2,newcol3

A CTE is a valid approach, giving additional options.
For a simple case like this a plain subquery is simpler and slightly faster.
SELECT *, (newcol2 * newcol3) AS newcol4
FROM (
SELECT CASE column1
WHEN something THEN 10
ELSE 20
END AS newcol1
,CASE column12
WHEN something THEN 30
ELSE 40
END AS newcol2
,COUNT(column3) AS newcol3
FROM table
GROUP BY 1, 2
) AS sub
BTW: I removed newcol3 from GROUP BY, since you are running the aggregate function count() on it, which is slightly nonsensical.

Related

What happens when I pass an CASE expression to the LAST_VALUE function

My LAST_VALUE function looks somethin like this
LAST_VALUE(
CASE
WHEN statement_1 then 0
WHEN statement_2 then 1
WHEN statement_3 then 0
ELSE NULL
END IGNORE NULLS) OVER (PARTITION BY column1 ORDER BY column2)
Can someone explains what value is the LAST_VALUE supposed to return if there is expression.
I understand what happens when a column is passed, but incase of such expressions no clue whatsoever.
You can find the description and examples at https://docs.oracle.com/cd/E11882_01/server.112/e41084/functions085.htm#SQLRF00655
Regards...
Btw. the CASE ... END structure is just as you are selecting values from the table field but with If condition. It doesn't affect the LAST_VALUE function.
It is something like this:
CASE WHEN ID = 1 THEN 10 WHEN ID = 2 THEN 20 ELSE 99 END
where ID = 1 is Statement1, ID = 2 is Statement2 ... and so on..
The same as if your case expression were itself a column...
SELECT
*,
LAST_VALUE(new_column IGNORE NULLS)
OVER (PARTITION BY column1
ORDER BY column2
)
FROM
(
SELECT
*,
CASE
WHEN statement_1 then 0
WHEN statement_2 then 1
WHEN statement_3 then 0
ELSE NULL
END
AS new_column
FROM
your_table
)
sub_query

Merging two columns but only unique combinations

I have two columns, each with identification numbers that have been brought in from different datasheets.
I want to combine this into one column with both identification numbers if they are different, but only one of the identification numbers if they are the same.
I'm using SELECT DISTINCT CONCAT(column 1, column 2) AS column 3 to combine the columns, but can not filter out UNIQUE combinations.
When I try WHERE column 1 <> column 2, I get an error message.
Any suggestions?
You can use CASE WHEN to test for conditions:
SELECT DISTINCT CASE WHEN column1 = column2 THEN column1
ELSE CONCAT(column1, column2)
END AS column3
FROM table1
try this using IIF or CASE and CONCAT
select
distinct
iif(col1<>col2,concat(col1,col2),col1) [myid]
from mytable
or
select
distinct
case when col1<>col2 then
concat(col1,col2)
else col1 end [myid]
from mytable
You should do something like:
SELECT DISTINCT CASE WHEN column1 = column2 THEN column1
ELSE column1 + '|' + column2
END AS combinedColumn
FROM table1
Consider the following chart:
column1 column2 column1+column2 column1+'|'+column2
12 34 1234 12|34
123 4 1234 123|4
1234 1234 1234 1234
Also, column1+column2 loses some information - what the original parts were.

Select NULL if a calculated column value is negative in SQL

I have the following piece of sql below. The second line (commented out) contains my addition of a check to see if the calculation returns a negative value in which case it should select NULL. This case is within a block of multiple other case statements. Since my approach means running the same calculation twice is there a better alternative or more efficient method to selecting NULL if the value of this calculated column is negative, rather than doing two similar calculations?
Thanks
CASE
WHEN M.ALPHA = 'B' OR T.CT IN (0.001, 0.002) THEN NULL
-- WHEN ((M.VAL / NULLIF (M.VAL2, 0)) / (NULLIF (T.VAL, 0) / T.VAL2)) < 0 THEN NULL
ELSE (M.VAL / NULLIF (M.VAL2, 0)) / (NULLIF (T.VAL, 0) / T.VAL2)
END As WORLD
You could move the calculation to a subquery. For example:
select case
when CalculatedColumn > 42 then 'Hot'
when CalculatedColumn < 42 then 'Cold'
else 'Answer'
end as Description
from (
select 2 * col1 + 3 as CalculatedColumn
from YourTable
) SubQuery
Sometimes it's clearer to define the subquery in a with clause:
; with SubQuery as
(
select 2 * col1 + 3 as CalculatedColumn
from YourTable
) SubQuery
select case
when CalculatedColumn > 42 then 'Hot'
when CalculatedColumn < 42 then 'Cold'
else 'Answer'
end as Description
from SubQuery

SQL: Alias Column Name for Use in CASE Statement

Is it possible to alias a column name and then use that in a CASE statement? For example,
SELECT col1 as a, CASE WHEN a = 'test' THEN 'yes' END as value FROM table;
I am trying to alias the column because actually my CASE statement would be generated programmatically, and I want the column that the case statement uses to be specified in the SQL instead of having to pass another parameter to the program.
This:
SELECT col1 as a,
CASE WHEN a = 'test' THEN 'yes' END as value
FROM table;
...will not work. This will:
SELECT CASE WHEN a = 'test' THEN 'yes' END as value
FROM (SELECT col1 AS a
FROM TABLE)
Why you wouldn't use:
SELECT t.col1 as a,
CASE WHEN t.col1 = 'test' THEN 'yes' END as value
FROM TABLE t;
...I don't know.
I think that MySql and MsSql won't allow this because they will try to find all columns in the CASE clause as columns of the tables in the WHERE clause.
I don't know what DBMS you are talking about, but I guess you could do something like this in any DBMS:
SELECT *, CASE WHEN a = 'test' THEN 'yes' END as value FROM (
SELECT col1 as a FROM table
) q
#OMG Ponies - One of my reasons of not using the following code
SELECT t.col1 as a,
CASE WHEN t.col1 = 'test' THEN 'yes' END as value
FROM TABLE t;
can be that the t.col1 is not an actual column in the table. For example, it can be a value from a XML column like
Select XMLColumnName.value('(XMLPathOfTag)[1]', 'varchar(max)')
as XMLTagAlias from Table
It should work. Try this
Select * from
(select col1, col2, case when 1=1 then 'ok' end as alias_col
from table)
as tmp_table
order by
case when #sortBy = 1 then tmp_table.alias_col end asc
I use CTEs to help compose complicated SQL queries but not all RDBMS' support them. You can think of them as query scope views. Here is an example in t-sql on SQL server.
With localView1 as (
select c1,
c2,
c3,
c4,
((c2-c4)*(3))+c1 as "complex"
from realTable1)
, localView2 as (
select case complex WHEN 0 THEN 'Empty' ELSE 'Not Empty' end as formula1,
complex * complex as formula2
from localView1)
select *
from localView2
Nor in MsSql
SELECT col1 AS o, e = CASE WHEN o < GETDATE() THEN o ELSE GETDATE() END
FROM Table1
Returns:
Msg 207, Level 16, State 3, Line 1
Invalid column name 'o'.
Msg 207, Level 16, State 3, Line 1
Invalid column name 'o'.
However if I change to CASE WHEN col1... THEN col1 it works
If you write only equal condition just:
Select Case columns1 When 0 then 'Value1'
when 1 then 'Value2' else 'Unknown' End
If you want to write greater , Less then or equal you must do like this:
Select Case When [ColumnsName] >0 then 'value1' When [ColumnsName]=0 Or [ColumnsName]<0 then
'value2'
Else
'Unkownvalue' End
From tablename
Thanks
Mr.Buntha Khin
SELECT
a AS [blabla a],
b [blabla b],
CASE c
WHEN 1 THEN 'aaa'
WHEN 2 THEN 'bbb'
ELSE 'unknown'
END AS [my alias],
d AS [blabla d]
FROM mytable
Not in MySQL. I tried it and I get the following error:
ERROR 1054 (42S22): Unknown column 'a' in 'field list'
In MySql, alice name may not work, therefore put the original column name in the CASE statement
SELECT col1 as a, CASE WHEN col1 = 'test' THEN 'yes' END as value FROM table;
Sometimes above query also may return error, I don`t know why (I faced this problem in my two different development machine). Therefore put the CASE statement into the "(...)" as below:
SELECT col1 as a, (CASE WHEN col1 = 'test' THEN 'yes' END) as value FROM table;
Yes, you just need to add a parenthesis :
SELECT col1 as a, (CASE WHEN a = 'test' THEN 'yes' END) as value FROM table;
make it so easy.
select columnnameshow = (CASE tipoventa
when 'CONTADO' then 'contadito'
when 'CREDITO' then 'cred'
else 'no result'
end) from Promocion.Promocion

Optimize help for sql query

We've got some SQL code I'm trying to optimize. In the code is a view that is rather expensive to run. For the sake of this question, let's call it ExpensiveView. On top of the view there is a query that joins the view to itself via a two sub-queries.
For example:
select v1.varCharCol1, v1.intCol, v2.intCol from (
select someId, varCharCol1, intCol from ExpensiveView where rank=1
) as v1 inner join (
select someId, intCol from ExpensiveView where rank=2
) as v2 on v1.someId = v2.someId
An example result set:
some random string, 5, 10
other random string, 15, 15
This works, but it's slow since I'm having to select from ExpensiveView twice. What I'd like to do is use a case statement to only select from ExpensiveView once.
For example:
select someId,
case when rank = 1 then intCol else 0 end as rank1IntCol,
case when rank = 2 then intCol else 0 end as rank2IntCol
from ExpensiveView where rank in (1,2)
I could then group the above results by someId and get almost the same thing as the first query:
select sum(rank1IntCol), sum(rank2Intcol)
from ( *the above query* ) SubQueryData
group by someId
The problem is the varCharCol1 that I need to get when the rank is 1. I can't use it in the group since that column will contain different values when rank is 1 than it does when rank is 2.
Does anyone have any solutions to optimize the query so it only selects from ExpensiveView once and still is able to get the varchar data?
Thanks in advance.
It's hard to guess since we don't see your view definition, but try this:
SELECT MIN(CASE rank WHEN 1 THEN v1.varCharCol1 ELSE NULL END),
SUM(CASE rank WHEN 1 THEN rank1IntCol ELSE 0 END),
SUM(CASE rank WHEN 2 THEN rank2IntCol ELSE 0 END)
FROM query
GROUP BY
someId
Note that in most cases for the queries like this:
SELECT *
FROM mytable1 m1
JOIN mytable1 m2
ON …
the SQL Server optimizer will just build an Eager Spool (a temporary index), which will later be used for searching for the JOIN condition, so probably these tricks are redundant.
select someId,
case when rank = 1 then varCharCol1 else '_' as varCharCol1
case when rank = 1 then intCol else 0 end as rank1IntCol,
case when rank = 2 then intCol else 0 end as rank2IntCol
from ExpensiveView where rank in (1,2)
then use min() or max in the enclosing query