Simplifying CASE WHEN SQL statement - sql

Im trying to improve the following CASE statement to calculate the difference only once. I do it to avoid negative numbers:
SELECT (CASE WHEN ((SELECT 100 - (SELECT COUNT(CustomerId) FROM Customers)) > 0)
THEN (SELECT 100 - (SELECT COUNT(CustomerId) FROM Customers))
ELSE (0)
END)
This not only looks stupid, but also is not thread-safe. I tried the following, but I get an error message "Invalid column name 'diff'."
SELECT (CASE WHEN ((SELECT 100 - (SELECT COUNT(CustomerId) FROM Customers) as diff) > 0)
THEN (diff)
ELSE (0)
END)
How can this be simplified? Is there an in-built SQL function that already does this job?
EDIT: Sorry, forgot to mention that the select statement is inside of a view declaration, so I cant declare variables.

If I follow your logic right, this should do it:
SELECT COALESCE ((SELECT 100 - COUNT(CustomerID)
FROM Customers
HAVING COUNT(CustomerID) < 100), 0)
Slightly more graceful.
If there was a row-based MAX() function, we could use that too. Of course you could write one easily enough.

DECLARE #diff int
SELECT #diff = 100 - COUNT(*) FROM Customers
SELECT CASE WHEN #diff > 0 THEN diff
ELSE 0
END as Diff

You could use a variable to start the result of the query so you don't have to execute it twice. For example:
DECLARE #CustomerCount INT
SELECT #CustomerCount = COUNT(CustomerId) FROM Customers
SELECT CASE WHEN (100 - #CustomerCount > 0)
THEN 100 - #CustomerCount
ELSE (0)
END

You are right, you only want to count once and you want to do it so it clearly shows what your logic is.
SELECT CASE WHEN cnt > 100 THEN 0
ELSE 100 - cnt END AS diff
FROM (SELECT COUNT(1) AS cnt
FROM Customers) AS CustomerCnt

Related

How to check unique values in SQL

I have a table named Bank that contains a Bank_Values column. I need a calculated Bank_Value_Unique column to shows whether each Bank_Value exists somewhere else in the table (i.e. whether its count is greater than 1).
I prepared this query, but it does not work. Could anyone help me with this and/or modify this query?
SELECT
CASE
WHEN NULLIF(LTRIM(RTRIM(Bank_Value)), '') =
(SELECT Bank_Value
FROM [Bank]
GROUP BY Bank_Value
HAVING COUNT(*) = 1)
THEN '0' ELSE '1'
END AS Bank_Key_Unique
FROM [Bank]
A windowed count should work:
SELECT
*,
CASE
COUNT(*) OVER (PARTITION BY Bank_Value)
WHEN 1 THEN 1 ELSE 0
END AS Bank_Value_Unique
FROM
Bank
;
It works also, but I found solution also:
select CASE WHEN NULLIF(LTRIM(RTRIM(Bank_Value)),'') =
(select Bank_Value
from Bank
group by Bank_Value
having (count(distinct Bank_Value) > 2 )) THEN '1' ELSE '0' END AS
Bank_Value_Uniquness
from Bank
It was missing "distinct" in having part.

Redshift SQL statement that will return 1 or 0 if the select statement returns any rows

I have the following select statement in Redshift that will return rows with certain values if the condition inside is met. I want to transform this into a DQ check which will return 1 (True) if no rows ae returned or 0 if any row is returned, but I do not know where I should apply the case statement.
Here is the select statement:
select * from (select brand,calendar_dt, product,
count(account) count from revenue_base
where player_days = 0 and volume_loc >0 group by brand,calendar_dt, product)
where count > 1000 and calendar_dt >='2020-07-12'
and calendar_dt < '2020-07-13'
Can you please offer me some ideas for this?
You may try using exists logic here:
select
case when not exists (
select 1 from
(
select brand, calendar_dt, product, count(account) as count
from revenue_base
where player_days = 0 and volume_loc > 0
group by brand, calendar_dt, product
) t
where calendar_dt >= '2020-07-12' and calendar_dt < '2020-07-13' and
count > 1000
)
then 1 else 0 end as result;
First, Redshift supports booleans, so case is not needed. Second, do the filtering on the date before the aggregation. This is usually faster.
Then, you can filter by the count using a having clause, so no subquery is needed:
select not exists (select 1
from revenue_base
where player_days = 0 and volume_loc > 0 and
calendar_dt >= '2020-07-12' and calendar_dt < '2020-07-13'
group by brand, calendar_dt, product
having count(*) > 1000
) as result

SQL Server sum and subtract prevent null results

I'm using this code:
SELECT
(SELECT SUM(TrnQty) AS Total
FROM InventoryTrans
WHERE InventoryItemID = (select MAX(InventoryItemID)
from inventorymaster)
AND CustomerID = '0') -
(SELECT SUM(TrnQty) AS Total
FROM InventoryTrans
WHERE InventoryItemID = (select MAX(InventoryItemID)
from InventoryMaster)
AND CustomerID > '0'
)
If the result of one them is NULL i take totally result NULL. How can I SUM if one of them is Null? Should I use CASE or something?
Here is a correct way to do it
SELECT Sum(CASE
WHEN customerid = '0' THEN trnqty
ELSE 0
END) - Sum(CASE
WHEN customerid > '0' THEN trnqty
ELSE 0
END) AS Total
FROM inventorytrans
WHERE inventoryitemid = (SELECT Max (inventoryitemid)
FROM inventorymaster)
EDIT:
I [#GordonLinoff] am providing a slightly simpler version of this answer. The use of conditional aggregation is correct, but there is a simpler way of writing it:
SELECT SUM(CASE WHEN it.customerid = '0' THEN it.trnqty
WHEN it.customerid > '0' THEN - it.trnqty
ELSE 0
END) as Total
FROM inventorytrans it
WHERE it.inventoryitemid = (SELECT Max(im.inventoryitemid)
FROM inventorymaster im
)
use
SELECT coalesce(SUM(TrnQty), 0) ...
You can also use
SELECT ISNULL(SUM(TrnQty),0)...
Simply wrap your existing inner select statements in an ISNULL function. It is simple to implement and your intent is clear, if your SELECT returns a null value, use 0.
SELECT
IsNull(
(
SELECT SUM(TrnQty) AS Total
FROM InventoryTrans
WHERE InventoryItemID =
(
select MAX (InventoryItemID) from inventorymaster
)
and CustomerID='0'
),0) -
IsNull(
(
SELECT SUM(TrnQty) AS Total
FROM InventoryTrans
WHERE InventoryItemID =
(
select MAX (InventoryItemID) from InventoryMaster
)
and CustomerID > '0'
),0)
Is this a great example of SQL, not really, but you can see how wrapping the select statements with IsNull is a minor change to your existing query to give you the results you need.
This query could be re-written into a single set based operation, here's my go at it:
SELECT SUM(CASE customerid WHEN '0' THEN -TrnQty ELSE TrnQty END) AS Total
FROM InventoryTrans
WHERE InventoryItemID =
(
select MAX (InventoryItemID) from inventorymaster
)
It is not necessary in this new query to check for null, (either with IsNull or Coalesce) unless you need to ensure that the end result is not null, this query will only return null if ALL of the TrnQty fields are also null. If that is a problem for you logic, wrap the SUM function in an IsNull function as well.

Getting true/false from SQL query

I need to determine whether a customer has bought 100 or more unique drinks using a SQL query and so far I've done:
SELECT COUNT(DISTINCT beer_id)
FROM Orders
WHERE patron_email='smith#gmail.com'
How would I test whether the result of this is 100 or more?
Update: Sorry for the vague question, I need to use standard SQL.
This works in MySQL, not sure about others:
SELECT COUNT(DISTINCT beer_id) > 100
FROM Orders
WHERE patron_email='smith#gmail.com'
Just put a boolean condition in the SELECT. It will evaluate to 0 for false or 1 for true.
By using the having clause as in:
SELECT COUNT(DISTINCT beer_id)
FROM Orders
WHERE patron_email='smith#gmail.com'
having count(distinct beer_id) > 100
You could use CASE with a sub-query which produces a single row:
SELECT CASE WHEN (SELECT COUNT(DISTINCT beer_id)
FROM Orders
WHERE patron_email='smith#gmail.com') > 100
THEN 'More than 100' ELSE 'Not more than 100' END As ColumnName
Try this:
SELECT COUNT(beer_id)
FROM Orders
WHERE patron_email='smith#gmail.com'
group by beer_id
having COUNT(beer_id) > 100
Assuming T-SQL
DECLARE #result AS INT
DECLARE #boolean AS INT
SET #result = ( SELECT COUNT(DISTINCT beer_id)
FROM Orders
WHERE patron_email='smith#gmail.com')
IF #result > 100
BEGIN
SET #boolean = 1
END
ELSE
BEGIN
SET #boolean = 0
END
or one more :
select case when (count(distinct beer_id)>100) then 1
else 0 end
from orders where patron_email='.....'
The EXISTS operator yields a boolean:
SELECT EXISTS (
SELECT 12345
FROM orders
GROUP BY customer_id
HAVING COUNT(beer_id) > 100
) AS at_least_one_customer_ordered_100_different_beers
;

force a ceiling to count(*) in sql query

I am using a subquery to return a count as an integer value to my main query. This query is used to rebind an ASP.NET DataGrid and I have only two characters width available for this column. I want to restrict the width to two characters. So, I want to set a value of 99 when the count exceeds 99. I can't figure a way to do this? I can't see how to apply a case statement here.
SELECT
MEMB_ID,
MEMB_Name,
SELECT COUNT(*)
FROM SessionOrder
WHERE SessionOrder.SORD_MEMB_ID = m.MEMB_ID
And SessionOrder.SORD_NumberCompleteDownloads <> 0
As MEMB_Downloads,
MEMB_JoinDate
FROM Member
How can this be done?
Replace
COUNT(*)
With
CASE WHEN COUNT(*) > 99 THEN 99 ELSE COUNT(*) END AS YourColumnName
The CASE expression can look like this:
CASE WHEN COUNT(*) > 99 THEN 99 ELSE COUNT(*) END
There appear to be a couple of errors with your existing query (for example m is not defined). With these errors corrected and the above change made the resulting query could look like this:
SELECT
MEMB_ID,
MEMB_Name,
(
SELECT CASE WHEN COUNT(*) > 99 THEN 99 ELSE COUNT(*) END
FROM SessionOrder
WHERE SessionOrder.SORD_MEMB_ID = MEMB_ID
AND SessionOrder.SORD_NumberCompleteDownloads <> 0
) AS MEMB_Downloads,
MEMB_JoinDate
FROM Member
This might be a bit more efficient. As it can stop scanning rows once the 99th is reached.
SELECT MEMB_ID ,
MEMB_Name,
( SELECT COUNT(*)
FROM (
SELECT TOP 99 *
FROM SessionOrder
WHERE SessionOrder.SORD_MEMB_ID = MEMB_ID
AND SessionOrder.SORD_NumberCompleteDownloads <> 0
)
Top99
) AS MEMB_Downloads,
MEMB_JoinDate
FROM Member
Rather than change the COUNT(*) result, better count at most 99:
SELECT
MEMB_ID,
MEMB_Name,
(SELECT COUNT(*)
FROM (
SELECT TOP(99) *
FROM SessionOrder
WHERE SessionOrder.SORD_MEMB_ID = m.MEMB_ID
And SessionOrder.SORD_NumberCompleteDownloads <> 0)
as TOP99_Downloads)
As MEMB_Downloads,
MEMB_JoinDate
FROM Member;
This way you avoid counting all the downloads when you'll only display 99 anyway. Of course, one would ask what is the point of displaying a value if is incorrect to start with and why not make your UI layer capable of displaying 'more than 99'.
CASE it should be ...
or double UNION as
SELECT
MEMB_ID,
MEMB_Name,
SELECT COUNT(*) AS WC
FROM SessionOrder
WHERE SessionOrder.SORD_MEMB_ID = m.MEMB_ID
And SessionOrder.SORD_NumberCompleteDownloads <> 0
And WC =< 99
As MEMB_Downloads,
MEMB_JoinDate
FROM Member
UNION
SELECT
MEMB_ID,
MEMB_Name,
99 AS WC
FROM SessionOrder
WHERE SessionOrder.SORD_MEMB_ID = m.MEMB_ID
And SessionOrder.SORD_NumberCompleteDownloads <> 0
And WC > 99
As MEMB_Downloads,
MEMB_JoinDate
FROM Member