How to Group By in SQL Server Query - sql

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.

Related

How to combine 2 SQL statements into one table

I have 2 SQL statements to look up successful transactions and failed transactions.
SELECT COUNT (code_reseller) as trx_success, kode_reseller
FROM transaksi
where status = '20' AND CAST (date_entri AS DATE) = CAST (GETDATE() AS DATE)
group by code_reseller
ORDER BY trx_success DESC
AND
SELECT COUNT (code_reseller) as trx_fail, kode_reseller
FROM transaksi
where status > '20' AND CAST (date_entri AS DATE) = CAST (GETDATE() AS DATE)
group by code_reseller
ORDER BY trx_fail DESC
How to combine into one table with 3 columns result with code_reseller, trx_success and trx_fail?
Use conditional aggregation and combine the queries:
SELECT
kode_reseller,
COUNT(CASE WHEN status = '20' THEN 1 END) AS trx_success,
COUNT(CASE WHEN status > '20' THEN 1 END) AS trx_fail
FROM transaksi
WHERE
CAST(date_entri AS DATE) = CAST(GETDATE() AS DATE)
GROUP BY
kode_reseller;
The strategy here is to move the filtering on the status column which previously appeared in the two WHERE clauses into the conditional counts in the SELECT clause. The restriction on date_entri can stay there, since both queries have it.
As suggested by #Dale k, you can do it like this.
You cannot add order by inside, so create an alias table and give order by condition.
SELECT *
FROM
(
SELECT COUNT (code_reseller) as trx_success, kode_reseller
FROM transaksi
WHERE status = '20' AND CAST (date_entri AS DATE) = CAST (GETDATE() AS DATE)
GROUP BY code_reseller
UNION ALL
SELECT COUNT (code_reseller) as trx_fail, kode_reseller
FROM transaksi
WHERE status > '20' AND CAST (date_entri AS DATE) = CAST (GETDATE() AS DATE)
GROUP BY code_reseller
) a
ORDER BY a.trx_success DESC --here we get first select query table' column name and datatype and no of column will be same required in union/union all

How to use CASE WHEN in group by

I want to use group by for the table NRW_MONTH_DATA.
SELECT [OBJECT_ID]
,[YEAR_MONTH]
,[SELLING_AMOUNT]
,[DEFAULT_SELLING_DATA]
,[LOCK_SELLING_AMOUNT]
,[RGCB]
,[ICKZ]
,[YCKZ]
FROM [dbo].[NRW_MONTH_DATA]
IF LOCK_SELLING_AMOUNT is 0 then group by OBJECT_ID and calculate the sum of [RGCB],[ICKZ] and [YCKZ]
SELECT #SELLING_AMOUNT=(ISNULL(SUM(YCKZ),0)+ISNULL(SUM(RGCB),0)+ ISNULL(SUM(ICKZ),0))
FROM [dbo].[NRW_MONTH_DATA]
WHERE OBJECT_ID=#OBJECT_ID
AND YEAR_MONTH >=#SELLING_CENSUS_START_YM
AND YEAR_MONTH <=#SELLING_CENSUS_END_YM
GROUP BY OBJECT_ID
Now I want to add a condition that if LOCK_SELLING_AMOUNT is 1 , I need to
SELECT #SELLING_AMOUNT=ISNULL(SUM(DEFAULT_SELLING_DATA),0)
ELSE use original result to calculate the sum of the 3 columns.
I use CASE WHEN but is seems that I could not use it in group by
SELECT #SELLING_AMOUNT=
CASE LOCK_SELLING_AMOUNT WHEN 1 THEN SELLING_AMOUNT
ELSE (ISNULL(SUM(YCKZ),0)+ISNULL(SUM(RGCB),0)+ ISNULL(SUM(ICKZ),0))
END
The error is like
The column'dbo.NRW_MONTH_DATA.LOCK_SELLING_AMOUNT' in the select list is invalid because the column is not included in the aggregate function or GROUP BY clause.
Thank you in advance.
I need the group by to calculate the sum of them. Each row has an object_id and a LOCK_SELLING_AMOUNT and other columns for one month, I want to use group to calculate the sum during month span.
It works well when I do not consider the LOCK_SELLING_AMOUNT
First, you don't want GROUP BY. So just use:
SELECT #SELLING_WATER = (COALESCE(SUM(YCKZ), 0) + COALESCE(SUM(RGCB), 0)+ COALESCE(SUM(ICKZ), 0))
FROM [dbo].[NRW_MONTH_DATA]
WHERE OBJECT_ID=#OBJECT_ID AND
YEAR_MONTH >= #SELLING_CENSUS_START_YM
YEAR_MONTH <= #SELLING_CENSUS_END_YM;
Now, the problem is that a column can change values on different rows. So, what row does LOCK_SELLING_AMOUNT come from? We could assume it is the same on all rows. Or perhaps you want an aggregation function:
SELECT #SELLING_WATER = (CASE WHEN MAX(LOCK_SELLING_AMOUNT) = 1
THEN MAX(CASE WHEN LOCK_SELLING_AMOUNT = 1 THEN SELLING_AMOUNT END)
ELSE (COALESCE(SUM(YCKZ), 0) + COALESCE(SUM(RGCB), 0)+ COALESCE(SUM(ICKZ), 0))
END)
FROM [dbo].[NRW_MONTH_DATA]
WHERE OBJECT_ID=#OBJECT_ID AND
YEAR_MONTH >= #SELLING_CENSUS_START_YM
YEAR_MONTH <= #SELLING_CENSUS_END_YM;

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);

Using Conditional Aggregation in SubQuery

Thanks to the help of another user, I was able to use Conditional Aggregation to get the data point I need. I now need to implement this into an existing query in order to get an SLA % for a date range (rather than each package). Previous post for reference: Pull a DATEDIFF between Rows with Distinct value and WHERE Clause
The below query was used when the assumption that the 2 timestamps in 'PackageTable' were accurate enough to calculate SLA. Since I found out they were not, I have to run the query on a different table (PackageTable_Audit) that basically records events in a row when a package moves from LifeCycleStatusId = 1 (creation) to LifeCycleStatusId = 3 (Assigned) to LifeCycleStatusId = 5 (Completed). As such, the SLA adherence % is the amount of packages that were completed in X seconds / total packages. Since I can't use a simple DATEDIFF in a sub-query, and thus have to use the aggregate function to get a DATEDIFF between rows, I'm not sure how to work it into the query.
I've updated my old query with the Conditional Aggregate, but I get the following Error:
"Cannot perform an aggregate function on an expression containing an aggregate or a subquery."
Query:
-- VARIABLE DECLARATION AND INITIALIZATION
DECLARE #StartDate varchar(10);
DECLARE #EndDate varchar(10);
SET #StartDate = '2019-06-01';
SET #EndDate = '2019-06-31';
-- TABLE DECLARATION ##################################################
DECLARE #TABLE1 TABLE("No. Packages in SLA" INT, "Total Packages" INT, "SLA %" FLOAT)
--#####################################################################
-- WHAT GETS INSERTED INTO TABLE 1
INSERT INTO #TABLE1
SELECT
A.NUM, A.DENOM, CAST(A.NUM AS FLOAT)/A.DENOM*100
FROM
(
-- COLUMN SELECTION. TWO NUMBERS WILL REPRESENT A NUM AND A DENOM
SELECT
(SELECT SUM(CASE
WHEN
datediff(second, MAX(CASE WHEN LifeCycleStatusId = 2 THEN rowDateModified END),
MAX(CASE WHEN LifeCycleSTatusId = 5 THEN rowDateModified END)
) < 172800
THEN 1
ELSE 0
END) AS IN_SLA
FROM PackageTable WITH (nolock)
WHERE lifecyclestatusid = 5
AND rowDateCreated BETWEEN #StartDate AND #EndDate)
AS NUM,
(SELECT COUNT(PackageGuid) As No_Packages
FROM PackageTable WITH (nolock)
WHERE lifecyclestatusid = 5
AND rowDateCreated BETWEEN #StartDate AND #EndDate)
AS DENOM
) A
SELECT "No. Packages in SLA", "Total Packages", "SLA %"
FROM #TABLE1

In Oracle SQL, how do you query the proportion of records of a certain value?

Say, you have a query like
SELECT COUNT(*), date FROM ORDERS GROUP BY date ORDER BY date
but you also want to have a third "phantom/dummy field", where it basically tells you the fraction of orders each day that are of a particular type (lets say "Utensils" and "Perishables").
I should say that there is an additional column in the ORDERS table that has the type of the order:
order_type
The third dummy column should do something like take the count of orders on a date that have the "Utensils" or the "Perishables" type (not XOR), then divide by the total count of orders of that day, and then round to 2 decimal points, and append a percentage sign.
The last few formatting things, aren't really important...all I really need to know is how to apply the logic in valid PLSQL syntax.
Example output
4030 2012-02-02 34.43%
4953 2012-02-03 16.66%
You can do something like
SELECT COUNT(*),
dt,
round( SUM( CASE WHEN order_type = 'Utensils'
THEN 1
ELSE 0
END) * 100 / COUNT(*),2) fraction_of_utensils_orders
FROM ORDERS
GROUP BY dt
ORDER BY st
If you find it easier to follow, you could also
SELECT COUNT(*),
dt,
round( COUNT( CASE WHEN order_type = 'Utensils'
THEN 1
ELSE NULL
END) * 100/ COUNT(*), 2) fraction_of_utensils_orders
FROM ORDERS
GROUP BY dt
ORDER BY st
To Add sum of orders of same type to query:
select
o.*,
(
select count(o2.OrderType)
from ORDERS o2
where o2.OrderType = o.OrderType
) as NumberOfOrdersOfThisType
from ORDERS o
To Add fraction of orders of same type to query:
(Check variable definition to make sure it is PL/SQL)
declare totalCount number
select count(*)
into totalCount
from ORDERS
select
o.*,
(
select count(o2.OrderType)
from ORDERS o2
where o2.OrderType = o.OrderType
) / totalCount as FractionOfOrdersOfThisType
from ORDERS o