Using boolean logic inside SUM function - sql

In SQL Server 2008, this query works:
SELECT
SUM(CAST(isredeemed AS TINYINT)) AS totalredeemed,
FROM rewards
GROUP BY merchantid
It gives you the number of redeemed rewards by merchant. The TINYINT cast is need to avoid the error Operand data type bit is invalid for sum operator.
Now I'd like to do a similar query, but one that only finds rewards redeemed in the last few days. I tried this:
SELECT
SUM(CAST((isredeemed & ( MIN(dateredeemed) > '2014-01-10 05:00:00')) AS TINYINT)) AS claimedthisweek,
FROM rewards
GROUP BY merchantid
and I get the error
Incorrect syntax near '>'.
I also tried replacing & with && and also with AND. But those don't work either.
How can I make the example work?

This question lacks detail to give an exact answer but you need to use a derived table or subquery for the calculation.
something like this
SELECT r1.merchantid, r2.claimedthisweek
FROM rewards r1
JOIN (
SELECT merchantid, SUM(CAST(isredeemed AS INT)) claimedthisweek
FROM rewards
GROUP BY merchantid
HAVING MIN(dateredeemed) > '20140101'
) r2

This should be on the having clause, like this:
SELECT
SUM(CAST((isredeemed AS TINYINT)) AS claimedthisweek
FROM rewards
GROUP BY merchantid
HAVING MIN(dateredeemed) > '2014-01-10 05:00:00'

Any reason not to do the filtering at the "where" clause level? That should work so long as all rows you're aggregating match the same criteria:
SELECT
SUM(CAST(isredeemed AS TINYINT)) AS claimedthisweek
FROM rewards
WHERE dateredeemed > '2014-01-10 05:00:00'
GROUP BY merchantid

I think you want this would work but it would count some twice:
SELECT
SUM(case when MIN(dateredeemed) > '2014-01-10 05:00:00' then 1 else 0 end )
FROM rewards
GROUP BY merchantid

Related

Adding an aggregate condition to get total count of sub-group

Thanks for the help on this matter, I'm new with SQL. I'm trying to get a sub-count of Jedi who had more than 2 padawans last month. I tried putting the condition in WHERE but I get an error saying I can't include aggregates in it. I also tried using a CASE but kept getting a syntax error there too. Any help on this would be incredible. Thank you so much!
SELECT COUNT(DISTINCT old_republic.jedi_id), old_republic.region_id
FROM jedi_archives.old_repulicdata old_republic
WHERE old_republic.republic_date >= '2022-06-01' AND old_republic.republic_date <= '2022-06-30' AND COUNT(old_republic.padawan)>2
GROUP BY old_republic.region_id
ORDER BY old_republic.region_id
SELECT old_republic.jedi_id CASE (
WHEN Count(old_republic.padawan)>2
THEN 1
ELSE 0 End), old_republic.region_id
FROM jedi_archives.old_repulicdata old_republic
WHERE old_republic.republic_date >= '2022-06-01' AND old_republic.republic_date <= '2022-06-30'
GROUP BY old_republic.region_id
ORDER BY old_republic.region_id
I can't comment to ask for a fiddle, but from what you've written, you're probably looking for the HAVING clause.
Assuming that padawan denotes the number of Padawans:
SELECT region_id, jedi_id, sum(padawan)
FROM jedi_archives.old_republicdata
WHERE republic_date >= '2022-06-01'
AND republic_date <= '2022-06-30'
GROUP BY region_id, jedi_id
HAVING sum(padawan) > 2;
This query will return the sum of Padawans for each Jedi per region who had more than two Padawans last month in one region (if you don't want to take the region into account, remove it from the SELECT and GROUP BY clause). Other Jedis won't appear in the result.
You can use the CASE expression, too, in order to indicate whether a Jedi had more than two padawans:
SELECT region_id, jedi_id,
CASE WHEN sum(padawan) > 2 THEN 1 ELSE 0 END AS more_than_2_padawans
FROM jedi_archives.old_republicdata
WHERE republic_date >= '2022-06-01'
AND republic_date <= '2022-06-30'
GROUP BY region_id, jedi_id;
I'm not entirely sure without sample data. But I think using the HAVING clause could solve your question.
SELECT COUNT(jedi_id) as jedi_id, region_id FROM tableA
WHERE republic_date between '2022-05-20' and '2022-05-25'
GROUP BY region_id
having padawan > 2
db fiddle

SQL with as expression shows multiple results

I am writing a SQL query using with as expression. I always get a result in the square of what I required.
This is my query:
DECLARE #MAX_DATE AS INT
SET #MAX_DATE = (SELECT DATEPART(MONTH,FECHA) FROM ALBVENTACAB WHERE NUMALBARAN IN (SELECT DISTINCT MAX(NUMALBARAN) FROM ALBVENTACAB));
;WITH TABLE_LAST AS (
SELECT CONCAT(DATEPART(MONTH,FECHA),'-',DATEPART(YEAR,FECHA)) as LAST_YEAR_MONTH
,SUM(TOTALNETO) AS LAST_YEAR_VALUE
FROM ALBVENTACAB
WHERE DATEPART(YEAR,CURRENT_TIMESTAMP) -1 = DATEPART(YEAR,FECHA) AND NUMSERIE LIKE 'A%'
AND DATEPART(MONTH,FECHA) <= #MAX_DATE
GROUP BY CONCAT(DATEPART(MONTH,FECHA),'-',DATEPART(YEAR,FECHA))
)
,TABLE_CURRENT AS(
SELECT CONCAT(DATEPART(MONTH,FECHA),'-',DATEPART(YEAR,FECHA)) as CURR_YEAR_MONTH
,SUM(TOTALNETO) AS CURR_YEAR_VALUE
FROM ALBVENTACAB
WHERE DATEPART(YEAR,CURRENT_TIMESTAMP) <= DATEPART(YEAR,FECHA) AND NUMSERIE LIKE 'A%'
GROUP BY CONCAT(DATEPART(MONTH,FECHA),'-',DATEPART(YEAR,FECHA))
)
SELECT *
FROM TABLE_CURRENT, TABLE_LAST
When I run the query I get exactly the square of the result.
I want to compare sale monthly with last year.
2-2020 814053.3 2-2019 840295.1
1-2020 1094993.65 2-2019 840295.1
3-2020 293927.3 2-2019 840295.1
2-2020 814053.3 1-2019 1050701.68
1-2020 1094993.65 1-2019 1050701.68
3-2020 293927.3 1-2019 1050701.68
2-2020 814053.3 3-2019 887776.1
1-2020 1094993.65 3-2019 887776.1
3-2020 293927.3 3-2019 887776.1
I should get only 3 rows instead of 9 rows.
You need to properly join your two CTE - the way you're doing it now, you're getting a Cartesian product of each row in either CTE together.
Do something like:
*;WITH TABLE_LAST AS
( ....
),
TABLE_CURRENT AS
( ....
)
SELECT *
FROM TABLE_CURRENT curr
INNER JOIN TABLE_LAST last ON (some join condition here)
What that join condition is going to be - I have no idea, and cannot tell from your question - but you have to define how these two sets of data "connect" ....
It could be something like:
SELECT *
FROM TABLE_CURRENT curr
INNER JOIN TABLE_LAST last ON curr.CURR_YEAR_MONTH = last.LAST_YEAR_MONT
or whatever else makes sense in your situation - but basically, you need to somehow "tie together" these two sets of data and get only those rows that make sense - not just every row from "last" combined with every row from "curr" ....
While you already got the answer on how to join the two results, I thought I'd tell you how to typically approach such problems.
From the same table, you want two sums on different conditions (different years that is). You solve this with conditional aggregation, which does just that: aggregate (sum) based on a condition (year).
select
datepart(month, fecha) as month,
sum(case when datepart(year, fecha) = datepart(year, getdate()) then totalneto end) as this_year,
sum(case when datepart(year, fecha) = datepart(year, getdate()) -1 then totalneto end) as last_year
from albventacab
where numserie like 'A%'
and fecha > dateadd(year, -2, getdate())
group by datepart(month, fecha)
order by datepart(month, fecha);

How to Group By in SQL Server Query

I'm using this query to get the Sum of SaleAmount for each type (SOType) of Sale Invoices.
I am getting the result but the result is not grouped by SOType. Have tried to use Group by Outside the query after where condition but getting an error as
"Column 'SaleInvoices.InvoiceID' is invalid because it is not
contained in either aggregate or group by function".
DECLARE #fromDate Datetime = '2019/05/23'
DECLARE #toDate Datetime = '2019/10/25'
DECLARE #isKpi int = '1'
SELECT (
(Select Sum((Isnull(I.Quantity,0)*Isnull(I.SalePrice,0))+((Isnull(I.Quantity,0)*Isnull(I.SalePrice,0) - I.Discount) *(I.TAX/100)))
from ItemsSold as I
where I.InvoiceId= S.InvoiceID and I.InvoiceType='Sale Invoice'
) -
(Select isnull(Sum((Isnull(I.Quantity,0)*Isnull(I.SalePrice,0))+((Isnull(I.Quantity,0)*Isnull(I.SalePrice,0) - I.Discount)*(I.TAX/100))),0)
from ItemsSold as I
where I.InvoiceId= S.InvoiceID and I.InvoiceType='Sale Return'
)) as Total
,S.SOType as SOType
FROM SaleInvoices AS S
where S.OrderDate>=Convert(VARCHAR,#fromDate,111) and S.OrderDate<=Convert(varchar,#toDate,111)
You want conditional aggregation. The logic should look something like this:
select s.SOType,
sum(case when i.invoicetype = 'Sale Invoice'
then (I.Quantity * I.SalePrice) * (1 - i.discount) * i.tax / 100.0
when i.invoicetype = 'Sale Return'
then - (I.Quantity * I.SalePrice) * (1 - i.discount) * i.tax / 100.0
end) as Total
from SaleInvoices s join
ItemsSold i
on i.InvoiceId= s.InvoiceID
where s.OrderDate >= #fromDate and
s.OrderDate <= #toDate
group by s.SOType ;
I'm not sure I got the arithmetic correct.
Notes:
The group by clause defines the rows being returned by the query. If you want one row per SOType then you want to GROUP BY SOType.
Use date comparisons and functions for dates. It is absurd to convert a date to a string to compare to a date.
You probably don't need COALESCE() or ISNULL() to handle NULL values. These are generally ignored by aggregation functions.

using count in a subquery and getting errors

i have line by line data in a table and i need to net of cancellations from sales and produce a report grouping on a scheme identifier. i.e i need to find all the sales and subtract all the cancellatsion to prduce a net sales figure.
i am trying to use the query below but i'm getting errors.
select insscheme, ((select count(quote_id) where (sale = '1')) - (select count(quote_id) where cancellation = '1')) as sales from policys
group by insscheme
order by insscheme
and i'm getting the error
Column 'policys.Sale' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Can anyone help me out with this?
You don't need any sub queries here. Just use COUNT and CASE.
SELECT insscheme,
COUNT(CASE WHEN sale = '1' AND cancellation <> '1' THEN 1 END) AS sales
FROM policys
GROUP BY insscheme
ORDER BY insscheme
I have assumed above that cancellation is not nullable. If it is use
COUNT(CASE WHEN sale = '1' THEN 1 END) -
COUNT(CASE WHEN cancellation = '1' THEN 1 END) AS sales
Perhaps this might work.
select insscheme, SUM(sale) - SUM(cancellation) as NetSales
from policys
group by insscheme
I don't see what the quote_id column has to do with your query. Are you querying one table or several? It would help if you could show us what your schema looks like with a brief discussion of your table layout.

MySql Count Query

I have this query that I am working on for a one time report. Basically what I am trying to do is find all the records where there are more than two transactions by a single account ID last month. I know it is probably something easy, my mind is just blanking.
SELECT streaming_transactions.account_id,
streaming_transactions_detail.transactions_description,
streaming_transactions_detail.transactions_detail_id,
streaming_transactions_detail.transactions_id,
streaming_transactions_detail.transactions_detail_amount,
streaming_transactions_detail.detail_type,
streaming_transactions_detail.products_id,
streaming_transactions_detail.products_levels_id,
streaming_transactions_detail.subscriptions_id,
streaming_transactions_detail.subscriptions_payment_options_id,
streaming_transactions_detail.modified
FROM streaming_transactions_detail
INNER JOIN streaming_transactions ON streaming_transactions_detail.transactions_id = streaming_transactions.transactions_id
WHERE streaming_transactions.charged = 1
AND streaming_transactions.timestamp_inserted > '2009-09-01 00:00:00'
AND streaming_transactions.account_id IN (
SELECT account_id
FROM streaming_transactions_detail
WHERE modified > '2009-09-01 00:00:00'
AND count(account_id) > 1)
AND streaming_transactions_detail.transactions_description LIKE '%Service Subscription%'
ORDER BY streaming_transactions.account_id DESC
I think you're almost there.
The subquery to get the transaction Ids is off, however
SELECT account_id
FROM streaming_transactions_detail
WHERE modified > '2009-09-01 00:00:00'
AND count(account_id) > 1)
-- Should be something like
SELECT account_id, COUNT(account_id)
FROM streaming_transactions_detail
WHERE modified > '2009-09-01 00:00:00'
GROUP BY account_id
HAVING count(account_id) > 1)
[unrelated] I'll throw in an unsolicited hint about style.
By using table aliases, you can improve the readabilty of the query. This can be done by optionally adding "AS xyz" where xyz is some short but mnemonic name, unique to this query, you can use xyz wherever in the query where you would use the long_named_table.
For example:
FROM streaming_transactions_detail AS D
and then
SELECT streaming_transactions.account_id,
streaming_transactions_detail.transactions_description,
streaming_transactions_detail.transactions_detail_id,
...
Can become (optionally, i.e. the "streaming_transactions_detail." still works too)
SELECT D.account_id,
D.transactions_description,
D.transactions_detail_id,
...