This question already has an answer here:
How to use an Alias in a Calculation for Another Field
(1 answer)
Closed 4 years ago.
select
category, count(category) as 'TotalCounts',
COUNT(case kind when 'avail'then 1 else null end) as 'avail',
Count(case kind when 'offers' then 1 else null end) as 'offers',
COUNT(CASE contactMethod WHEN 'SMS' then 1 else null END) as 'SMS',
COUNT(case contactMethod when 'call' then 1 else null end) as 'call',
CONVERT(varchar(254),COUNT (case when max_biz_status='A' OR
max_biz_status ='B' then 1 else null end) * 100 / count(category)) +'%'
as 'Percetange'
from reports
group by category
order by TotalCounts
Instead of calculating again in Convert method i want to use avail* 100 / TotalCounts like i did in order by when i used TotalCounts.
i tried:
CONVERT(varchar(254),avail * 100 / TotalCounts) +'%' as 'Percetange'
but i get 'invalid column name' for avail and TotalCounts
You can't do that because your TotalCounts column is made from your result set.
you can try to use a subquery to contain it then calculation.
if your mssql version support CONCAT function you can use it let the SQL clearer.
SELECT t1.*,CONCAT((max_biz_statusCnt * 100 /TotalCounts),'%')as 'Percetange'
FROM
(
select
category,
count(*) as 'TotalCounts',
COUNT(case kind when 'avail'then 1 else null end) as 'avail',
Count(case kind when 'offers' then 1 else null end) as 'offers',
COUNT(CASE contactMethod WHEN 'SMS' then 1 else null END) as 'SMS',
COUNT(case contactMethod when 'call' then 1 else null end) as 'call',
COUNT (case when max_biz_status='A' OR max_biz_status ='B' then 1 else null end) 'max_biz_statusCnt'
from reports
group by category
) t1
order by TotalCounts
You can't use avail or TotalCounts as you just created them, so they aren't in scope, using a common-table expression is one way to fix this:
WITH cte AS (
SELECT
category,
COUNT(category) AS TotalCounts,
COUNT(case kind WHEN 'avail' THEN 1 ELSE NULL END) AS avail,
COUNT(case kind WHEN 'offers' THEN 1 ELSE NULL END) AS offers,
COUNT(CASE contactMethod WHEN 'SMS' THEN 1 ELSE NULL END) AS SMS,
COUNT(case contactMethod WHEN 'call' THEN 1 ELSE NULL END) AS [call]
FROM
reports
GROUP BY
category)
SELECT
*,
CONVERT(varchar(254),avail * 100 / TotalCounts) +'%' AS Percetange --(sic)
FROM
cte
ORDER BY
TotalCounts;
Related
I have a table in SQL Server called ShippingDocSummary which contains all historical records for shipping documents. For a given document number, there can be any number of historical records/transactions. What I am trying to do is create a view that summarizes that table and calculates various column values. My issue comes when trying to get a specific value from the source table based on a calculated column value from the same source table.
Here is a simplified example of what the source table looks like:
DocNum DocHistID StatusCode StatusQty StatusDate
12345 10000001 AS1 2 2/16/2020
12345 10000002 D6T 2 4/20/2020
12345 10000003 COR 2 4/20/2020
12345 10000004 AS1 2 5/5/2020
My code to create my summary so far is as follows:
SELECT
DocNum,
SUM(CASE WHEN ShippingDocSummary.StatusCode LIKE 'AS_' THEN StatusQty ELSE 0 END) AS AS_QTY,
SUM(CASE WHEN ShippingDocSummary.StatusCode LIKE 'D6T' THEN StatusQty ELSE 0 END) AS D6T_QTY,
SUM(CASE WHEN ShippingDocSummary.StatusCode LIKE 'COR' THEN StatusQty ELSE 0 END) AS COR_QTY,
MAX(CASE WHEN ShippingDocSummary.StatusCode LIKE 'AS_' THEN DocHistID ELSE null END) AS LastAS_DHID
FROM ShippingDocSummary
GROUP BY DocNum
This gives me the following, which is correct:
DocNum AS_QTY D6T_QTY COR_QTY LastAS_DHID
12345 4 2 2 10000004
What I am trying to get next, as a column, is the StatusDate that correlates to the LastAS_DHID value that was calculated from the select (i.e. I need the next column to show '5/5/2020' since that date ties to the DocHistID '10000004').
In Power Bi, I'd use a lookupvalue function, but as I transfer my logic to the SQL server, I think I need to use a derived CASE WHEN type of query, but unsure of how to structure the syntax. Any help is greatly appreciated!
Maybe you have to create your on table and then do a left join. This being said, Try this:
SELECT A.DocNum,
A.AS_QTY,
A.D6T_QTY,
A.COR_QTY,
A.LastAS_DHID,
B.StatusDate
FROM (
SELECT DocNum,
SUM(CASE WHEN ShippingDocSummary.StatusCode LIKE 'AS_' THEN StatusQty ELSE 0 END) AS AS_QTY,
SUM(CASE WHEN ShippingDocSummary.StatusCode LIKE 'D6T' THEN StatusQty ELSE 0 END) AS D6T_QTY,
SUM(CASE WHEN ShippingDocSummary.StatusCode LIKE 'COR' THEN StatusQty ELSE 0 END) AS COR_QTY,
MAX(CASE WHEN ShippingDocSummary.StatusCode LIKE 'AS_' THEN DocHistID ELSE null END) AS LastAS_DHID
FROM ShippingDocSummary
GROUP BY DocNum) A
LEFT JOIN TABLE_WITH_DOCHIST_10000004 B
ON A.LastAS_DHID = B.DocHistID
SELECT DISTINCT
DocNum,
SUM(CASE WHEN StatusCode LIKE 'AS_' THEN StatusQty ELSE 0 END) OVER (PARTITION BY DocNum) AS AS_QTY,
SUM(CASE WHEN StatusCode LIKE 'D6T' THEN StatusQty ELSE 0 END) OVER (PARTITION BY DocNum) AS D6T_QTY,
SUM(CASE WHEN StatusCode LIKE 'COR' THEN StatusQty ELSE 0 END) OVER (PARTITION BY DocNum) AS COR_QTY,
FIRST_VALUE(StatusDate) OVER (PARTITION BY DocNum ORDER BY CASE WHEN StatusCode LIKE 'AS_' THEN DocHistID ELSE null END DESC) AS LastAS_DHID
FROM ShippingDocSummary
I've converted your aggregate functions to windowed versions with partitioning on the original grouping column. The select distinct is then necessary to actually collapse the rows to a single aggregated row. If first_value() isn't available on your platform there are other alternatives.
Here's one that worked for me as a scalar subquery:
(
SELECT StatusDate FROM ShippingDocSummary sds2
WHERE sds2.DocHistId =
MAX(
CASE WHEN ShippingDocSummary.StatusCode LIKE 'AS_'
THEN ShippingDocSummary.DocHistID ELSE null END
)
) AS LastAS_DHID
I have a query that looks like this:
SELECT store_id,
(CASE WHEN txns_A>0 AND txns_B=0 THEN 'A Only' WHEN txns_A=0 AND txns_B>0 THEN 'B Only' END) A_B_indicator,
sum(1) cnt_customers,
sum(spend_A+spend_B)/sum(txns_A+txns_B) avg_receipt
FROM(
SELECT store_id, cust_id
SUM(CASE WHEN A_B_indicator='A' THEN spend else 0 end) spend_A,
SUM(CASE WHEN A_B_indicator='B' THEN spend else 0 end) spend_B,
SUM(CASE WHEN A_B_indicator='A' THEN spend else 0 end) txns_A,
SUM(CASE WHEN A_B_indicator='B' THEN spend else 0 end) txns_B
FROM table1
GROUP BY store_id, cust_id
) table2;
However, this generates an error because store_id is not in a GROUP BY clause. When I rewrite the query to include a GROUP BY store_id clause, it complains that the aggregate columns are not in the Group By. However, if I add them by rewriting the Group By to be Group BY 1,2,3,4, this also generates an error (Not yet supported place for UDAF Sum).
How can I rewrite this query to be error-free?
You can write this as:
SELECT store_id,
(CASE WHEN SUM(txns_A) > 0 AND SUM(txns_B) = 0 THEN 'A Only'
WHEN SUM(txns_A) = 0 AND SUM(txns_B) > 0 THEN 'B Only'
END) as A_B_indicator,
COUNT(*) as cnt_customers,
SUM(spend_A+spend_B)/sum(txns_A+txns_B) as avg_receipt
FROM (SELECT store_id, cust_id
SUM(CASE WHEN A_B_indicator='A' THEN spend else 0 end) as spend_A,
SUM(CASE WHEN A_B_indicator='B' THEN spend else 0 end) as spend_B,
SUM(CASE WHEN A_B_indicator='A' THEN spend else 0 end) as txns_A,
SUM(CASE WHEN A_B_indicator='B' THEN spend else 0 end) as txns_B
FROM table1
GROUP BY store_id, cust_id
) table2
GROUP BY store_id;
I have this table
SELECT
[WeldStatus]
,PackageId
FROM [SPMS2].[dbo].[JointHistory]
How can I group by this query to calculate total testpackage and total accept and total remain?
For example for packageid=6030 I need this:
packageid total accept remain
6030 4 3 1
Use CASE and Group By
SELECT
PackageId,
count(*) as total,
sum(case [WeldStatus] when 'Accept' then 1 end) as accept,
sum(case [WeldStatus] when 'accept' then 0 else 1 end) as remain
FROM [SPMS2].[dbo].[JointHistory]
group by PackageId;
SELECT
packageid
,count(*) as 'total'
,sum(case when weldstatus = 'Accept' then 1 else 0 end) as 'accept'
,sum(case when weldstatus is null then 1 else 0 end) as 'remain'
FROM [SPMS2].[dbo].[JointHistory]
group by packageid
"sum(case when..." has saved me so many times. NULLs can be a pain but the case when saves you.
You can use CASE expression with in your aggregation to achieve your desired output.
SELECT
PackageId,
SUM (CASE WHEN WeldStatus = 'Accept' THEN 1 ELSE 0 END) AS AcceptCount,
SUM (CASE WHEN WeldStatus IS NULL THEN 1 ELSE 0 END) AS RemainCount,
COUNT (*) AS Total
FROM [SPMS2].[dbo].[JointHistory]
GROUP BY PackageId
select packageid, SUM(TOTAL), SUM(ACCEPT), SUM(REMAIN) from (
select packageid, 1 as TOTAL,
case
when WeldStatus='Accept' then 1 else 0
END as ACCEPT,
case
when WeldStatus IS NULL then 1 else 0
END as REMAIN
from JointHistory
) group by packageid
If WeldStatus only takes on the values of 'Accept' or NULL, then a simple method is:
SELECT PackageId, count(*) as total,
count(WeldStatus) as accept,
count(*) - count(WeldStatus) as remain
FROM [SPMS2].[dbo].[JointHistory] jh
GROUP BY PackageId;
I'm the first to admit that this relies on the assumption of having a single value or NULL.
SELECT PackageId,
COUNT(*) AS total,
SUM(CASE WHEN [WeldStatus] = 'Accept' THEN 1 END) AS accept,
SUM(CASE WHEN ISNULL([WeldStatus],'') = '' THEN 1 ELSE 0 END) AS remain
FROM [SPMS2].[dbo].[JointHistory]
group by PackageId;
My current code:
SELECT VENDOR.[VENDOR_NAME], DEVICE.[DEVICE_NAME], DEVICE.[PK_DEVICE],MODELDEVICE.[FK_MODELDEVICE_DEVICE],
COUNT(RESULT.[FK_RESULT_COMMAND]) AS TOTAL_TESTS,
COUNT(case when TYPERESULT.[TYPERESULT_NAME]='ERROR' then 1 else null end) as ERROR,
COUNT(case when TYPERESULT.[TYPERESULT_NAME]='OK' then 1 else null end) as OK,
COUNT(case when TYPERESULT.[TYPERESULT_NAME]='SKIP' then 1 else null end) as SKIP,
COUNT(DISTINCT PK_COMMAND) AS COMMAND_COUNT,
COUNT(DISTINCT RESULT_ORDER) AS RESULT_COUNT
The question is that I need another column with the result of dividing total ERROR results in TOTAL_TESTS results, and I donĀ“t know how to do that
I have a preference for using SUM() in this case, rather than COUNT(). I think you will see why, because the average is easy to calculate as well:
COUNT(*) AS TOTAL_TESTS,
SUM(case when TYPERESULT.[TYPERESULT_NAME] = 'ERROR' then 1 else 0 end) as ERROR,
SUM(case when TYPERESULT.[TYPERESULT_NAME] = 'OK' then 1 else 0 end) as OK,
AVG(case when TYPERESULT.[TYPERESULT_NAME] = 'ERROR' then 1.0 else 0 end) as ERROR,
. . .
Note the use of 1.0. This is because SQLite does integer division, so we need to pass in a non-integer value.
Just divide the 2 columns.
SELECT VENDOR.[VENDOR_NAME], DEVICE.[DEVICE_NAME], DEVICE.[PK_DEVICE],MODELDEVICE.[FK_MODELDEVICE_DEVICE],
COUNT(RESULT.[FK_RESULT_COMMAND]) AS TOTAL_TESTS,
COUNT(CASE WHEN TYPERESULT.[TYPERESULT_NAME] = 'ERROR' THEN 1 ELSE NULL END) AS ERROR,
COUNT(CASE WHEN TYPERESULT.[TYPERESULT_NAME] = 'OK' THEN 1 ELSE NULL END) AS OK,
COUNT(CASE WHEN TYPERESULT.[TYPERESULT_NAME] = 'SKIP' THEN 1 ELSE NULL END) AS SKIP,
COUNT(DISTINCT PK_COMMAND) AS COMMAND_COUNT,
COUNT(DISTINCT RESULT_ORDER) AS RESULT_COUNT,
(COUNT(CASE WHEN TYPERESULT.[TYPERESULT_NAME] = 'ERROR' THEN 1.0 ELSE NULL END))/(COUNT(RESULT.[FK_RESULT_COMMAND])) AS DivColumn
I have a table like this
TABLEMAIN
Q1 Name Group Zone Month Type
1 'N1' 'G1' 'Z1' 12 'T1'
4 'N1' 'G3' 'Z2' 12 'T6'
6 'N1' 'G1' 'Z5' 12 'T2'
3 'N2' 'G4' 'Z5' 12 'T4'
.
.
.
And I have something like this to get certain results
Query1:
select
(SUM(CASE Q1>=2 and Q1<=4 THEN 1 ELSE 0 END)) TOTAL,
(CASE WHEN Type = 'T1' THEN SUM(CASE WHEN Q1=4 THEN 1 ELSE 0 END)) T1TYPE,
(CASE WHEN Type = 'T1' THEN SUM(CASE WHEN Q1=4 THEN 1 ELSE 0 END)) T2TYPE,
Type,
**Zone,**
Month
from
TABLEMAIN
GROUP BY Type, **Zone,** Month;
Query2:
select
(SUM(CASE Q1>=2 and Q1<=4 THEN 1 ELSE 0 END)) TOTAL,
(CASE WHEN Type = 'T1' THEN SUM(CASE WHEN Q1=4 THEN 1 ELSE 0 END)) T1TYPE,
(CASE WHEN Type = 'T1' THEN SUM(CASE WHEN Q1=4 THEN 1 ELSE 0 END)) T2TYPE,
Type,
**Group,**
Month
from
TABLEMAIN
GROUP BY Type, **Group,** Month;
As you can see I group this table many times in many ways, but this part is the same in every query
select
(SUM(CASE Q1>=2 and Q1<=4 THEN 1 ELSE 0 END)) TOTAL,
(CASE WHEN Type = 'T1' THEN SUM(CASE WHEN Q1=4 THEN 1 ELSE 0 END)) T1TYPE,
(CASE WHEN Type = 'T1' THEN SUM(CASE WHEN Q1=4 THEN 1 ELSE 0 END)) T2TYPE,
Is there a better way to do this? I'm not sure if I can use a materialized view for this
Perhaps. You can do it all in one query, if you like by using grouping sets:
select SUM(CASE Q1>=2 and Q1<=4 THEN 1 ELSE 0 END) as TOTAL,
(CASE WHEN Type = 'T1' THEN SUM(CASE WHEN Q1=4 THEN 1 ELSE 0 END)) as T1TYPE,
(CASE WHEN Type = 'T1' THEN SUM(CASE WHEN Q1=4 THEN 1 ELSE 0 END)) as T2TYPE
Type, **Zone,**, **Group,** Month
from TABLEMAIN
GROUP BY GROUPING SETS((Type, **Zone,** Month), (Type, **Group,** Month));
This puts all the results in a single table.
I second with #GolezTrol comment. Would like to explain further.
SUBQUERY FACTORING is what you need. The WITH clause, or subquery factoring clause, is part of the SQL-99 standard and was added into the Oracle SQL syntax in Oracle 9.2. The WITH clause may be processed as an inline view or resolved as a temporary table. The advantage of the latter is that repeated references to the subquery may be more efficient as the data is easily retrieved from the temporary table, rather than being required by each reference.
WITH data AS(
<your subquery>
)
SELECT * FROM data
bla bla bla...