SQL : hiding a calculated column - sql

CREATE table orders
{
id integer,
product_id integer,
type VARCHAR(16)
}
SELECT
(SELECT COUNT(*) FROM orders) AS "Order Count",
-- I don't want total to show up
(SELECT COUNT(*) FROM orders WHERE product_id = 500) AS "total",
(SELECT COUNT(*) FROM orders WHERE product_id = 500 AND type = 'small') * 100 / "total" AS "% Small Sold",
(SELECT COUNT(*) FROM orders WHERE product_id = 500 AND type = 'medium') * 100 / "total" AS "% Medium Sold",
(SELECT COUNT(*) FROM orders WHERE product_id = 500 AND type = 'large') * 100 / "total" AS "% Large Sold"
FROM
orders
I have this SQL report. I have a number of columns and one of I'm creating to use to calculate my other columns, in this case "total". I don't want it to appear in the report though. Is there a way to code it in some other part of the query or mark it as hidden? I'm using Postgres.

You can use a common table expression (CTE) for your totals. Then select the fields you want to keep in your report from the CTE.
WITH totals AS (
SELECT
(SELECT COUNT(*) FROM orders) AS "Order Count",
-- I don't want total to show up
(SELECT COUNT(*) FROM orders WHERE product_id = 500) AS "total",
(SELECT COUNT(*) FROM orders WHERE product_id = 500 AND type = 'small') * 100 / "total" AS "% Small Sold",
(SELECT COUNT(*) FROM orders WHERE product_id = 500 AND type = 'medium') * 100 / "total" AS "% Medium Sold",
(SELECT COUNT(*) FROM orders WHERE product_id = 500 AND type = 'large') * 100 / "total" AS "% Large Sold"
FROM orders
)
SELECT
"Order Count",
"% Small Sold",
"% Medium Sold",
"% Large Sold"
FROM
totals

I'm not sure what you mean by hidden but I have to show you a better way to write this query
SELECT
COUNT(*) AS "Order Count",
-- I don't want total to show up
SUM(CASE WHEN PRODUCT_ID = 500 THEN 1 ELSE 0 END) AS "total",
SUM(CASE WHEN PRODUCT_ID = 500 AND type = 'small' THEN 1 ELSE 0 END) * 100 / SUM(CASE WHEN PRODUCT_ID = 500 THEN 1 ELSE 0 END) AS "% Small Soldl",
SUM(CASE WHEN PRODUCT_ID = 500 AND type = 'medium' THEN 1 ELSE 0 END) * 100 / SUM(CASE WHEN PRODUCT_ID = 500 THEN 1 ELSE 0 END) AS "% Medium Soldl",
SUM(CASE WHEN PRODUCT_ID = 500 AND type = 'large' THEN 1 ELSE 0 END) * 100 / SUM(CASE WHEN PRODUCT_ID = 500 THEN 1 ELSE 0 END) AS "% Large Soldl",
FROM orders
I expect you will see a significant increase in performance

Why don' t you filter first? It is decreasing your query performance.
Check this:
with maintab as (select case
when type is not Null then type
else 'total'
end type, count(*) cnt from orders
where product_id = 500 and type in ('small', 'medium', 'large')
group by rollup(type))
select type, cnt*100/(select cnt from maintab where type = 'total' ) percentage
from maintab
where type in ('small', 'medium', 'large');
If your table contains different values in product_id and type columns, of course you should go with this one:
with cnttab as (select count(*) cnt from orders), maintab as (select type, count(*) cnt from orders
where product_id = 500 and type in ('small', 'medium', 'large')
group by type)
select type, cnt*100/(select cnt from cnttab) percentage
from maintab;
You can compare explain plans.

Related

Reduce number of repeating SUM functions in query

How to reduce the number of SUM functions in my query?
SELECT P_NAME "Product name", (SUM(case when PR = 1 then QUANTITY end) -
SUM(case when PR = 2 then QUANTITY end)) "End balance",
CAST((SUM(case when PR = 1 then QUANTITY*PRICE end) - SUM(case when PR = 2 then
QUANTITY*PRICE end)) as decimal(13,2)) "End balance" FROM RPOD, DMS, DMZ
WHERE RPOD.KTOV=DMS.KTOV AND DMZ.NDM=DMS.DMZ_FK
GROUP BY P_NAME
ORDER BY P_NAME;
You can combine them:
SELECT P_NAME as "Product name",
SUM(case when PR = 1 then QUANTITY
when PR = 2 then - QUANTITY
end) as "End balance",
SUM(case when PR = 1 then QUANTITY * PRICE
when PR = 2 then - QUANTITY * PRICE
end) as "End balance/price",
FROM RPOD JOIN
DMS
ON RPOD.KTOV = DMS.KTOV JOIN
DMZ
ON DMZ.NDM = DMS.DMZ_FK
GROUP BY P_NAME
ORDER BY P_NAME;
Much more important aesthetics about SUM()s is learning to use proper, explicit JOIN syntax. Never use commas in the JOIN clause. Also, you should not give two columns in the result set the same name.

how can i set the alias of column`s sum value based on another column in same table in sql?

I have a table of Accounts, having columns:
Acc_Id, Transaction_TypeId, Amount
I want to get result as When Transaction_TypeId = 1 then Sum of Amount as 'Total Advance Payments'.
Else when Transaction_typeId = 2 then Sum of Amount as 'Total Reciepts'
Here is my SQL query:
SELECT SUM(Amount) AS 'Sum' , Transaction_TypeId INTO #temp1 FROM AccountDetailTable WHERE Account_MasterId = 1 GROUP BY Transaction_TypeId
SELECT Sum as 'Total Advance' from #temp1 WHERE #temp1.Transaction_TypeId = 1;
SELECT Sum as 'Total Cash Receipts' FROM #temp1 WHERE #temp1.Transaction_TypeId = 2;
DROP TABLE #temp1;
but this query returns me two different result sets. How can i get the values in same result sets?
Use a CASE expression:
SELECT SUM(CASE WHEN Transaction_TypeId = 1 THEN somecolumn END) as [Total Advance],
SUM(CASE WHEN Transaction_TypeId = 2 THEN somecolumn END) as [Total Cash Receipts]
FROM #temp1;
You should use CASE EXPRESSION like this:
SELECT
sum(case when #temp1.Transaction_TypeId = 1 then amount else 0 end) as 'Total Advance',
sum(case when #temp1.Transaction_TypeId = 2 then amount else 0 end) as 'Total Cash Receipts'
FROM #temp1

Divide and sum in SQL

I got this code and in this code I do a sum of the slow and fast driver. My Problem is I must divide this sum with the normal driver. I donĀ“t know how I can do a division in this statement:
Select *
FROM (
Select date as Datetime, tevent.name as Event, level = case
when levelname = 'High' then 'High'
when levelname = 'Normal' then 'Normal'
when shiftname = 'Low' then 'Low'
end, SUM(value) as sum
from tCount inner join tEvent ON tCount.eventid = tevent.id
where Name in ('Drive Fast', 'Drive Slow')
and date > getdate() -1
and tevent.Name in ('E01','E02','E03','E04','E05','E06','E07','E08')
and CalName = 'Drive'
group by tevent.name, date, levelname
) as s
PIVOT
(
SUM(sum)
FOR Event IN (E01,E02,E03,E04,E05,E06,E07,E08)
) as p
order by Datetime, level
And Then I put the same Select statement with the normal driver :
... from tCount inner join tEvent ON tCount.eventid = tevent.id
where Name in ('drive normal') ...
And I would like to make a division like this:
(Sum('drive fast' + 'drive slow')/Sum('drive normal')) * 100
There is a simpler way to include different cases in different sums inside a SQL statement: sum a case, like in the below calculation of percent:
Select ...
, SUM(case Name
when 'drive fast' then Value
when 'drive slow' then value
else 0 end)
/ SUM(case Name
when 'drive normal' then value
else 0 end) * 100 as percentage
from ...
where ...
group by ...;
As I lack data to test this code, I created a query on the CARS table SAS delivers as training material, implementing the same principle.
select Cylinders
, sum(case origin when 'USA' then EngineSize
when 'Asia' then EngineSize
else 0.0 end)
/ sum(case origin when 'Europe' then EngineSize
else 0.0 end)
* 100 as percentage
from sasHelp.cars
where Cylinders in (4, 5, 6, 12)
group by Cylinders

SQL counting aggregate query

I have the following query:
select prop_id
, sum(amount)bnp_spent
, (select count(*) from cost where cost_type = 'Direct Cost')direct
, (select count(*) from cost where cost_type = 'Burden Cost')burden
from cost
group by prop_id
The subqueries are NOT WHAT I WANT. By selecting from the cost table I get the total number of costs that are Direct or Burden for all props
What I want is the count of direct and burden costs for each prop_id
Any help is greatly appreciated.
Try this:
select prop_id, sum(amount) as bnp_spent,
sum(case when cost_type = 'Direct Cost' then 1 else 0 end) as direct,
sum(case when cost_type = 'Burden Cost' then 1 else 0 end) as burden
from cost
group by prop_id

Complex SQL query with group by and having

I am having a table orders
orders (
id int unsigned not null,
fcr_date TIMESTAMP,
completion_date TIMESTAMP,
factory_no varchar(255),
vendor_no varchar(255))
Please ignore the data type typos if any.
I want to write a sql query that helps me filter the data per vendor factory. The data to fetch includes the number of orders per vendor factory(a unique group of vendor_no, factory_no), vendor_no, factory_no and the percentage of orders for which fcr_date is greater than completion_date(so percentage = number of orders where fcr_date is greater than completion date / count of orders). After that i need to filter the data where percentage is greater than say 20%.
I wrote the following query:
SELECT vendor_no As vendor,
factory_no As factory,
COUNT(1) as count,
SUM(CASE WHEN fcr_date > completion_date THEN 1 ELSE 0 END) as filter_orders,
ROUND(filter_orders / count * 100, 4) as percent
FROM #orders
GROUP BY vendor_no,
factory_no
HAVING percent>20
but postgresql complains that it needs to have a column called percent in table to filter the results based on that. Any help is appreciated.
Thanks.
Change it to:
HAVING ROUND(filter_orders / count * 100, 4) > 20
Because percent isn't an actual column, you need to give it the calculation to perform the filter.
Edit
OK, looking at this further, you've got at least two ways to write this: the one I'd recommend is the first, which involves wrapping in a sub-query (as someone already suggested):
Option 1
SELECT vendor As vendor,
factory As factory,
[count],
ROUND(filter_orders / count * 100, 4) as [percent]
FROM
(
SELECT vendor_no As vendor,
factory_no As factory,
COUNT(1) as count,
SUM(CASE WHEN fcr_date > completion_date THEN 1 ELSE 0 END) as filter_orders
FROM #orders
GROUP BY vendor_no,
factory_no
) AS a
WHERE ROUND(filter_orders / count * 100, 4) > 20
Option 2
SELECT vendor_no As vendor,
factory_no As factory,
COUNT(1) as count,
SUM(CASE WHEN fcr_date > completion_date THEN 1 ELSE 0 END) as filter_orders,
ROUND(SUM(CASE WHEN fcr_date > completion_date THEN 1 ELSE 0 END) / count(1) * 100, 4) as [percent]
FROM #orders
GROUP BY vendor_no,
factory_no
HAVING ROUND(SUM(CASE WHEN fcr_date > completion_date THEN 1 ELSE 0 END) / count(1) * 100, 4) > 20
Wrap your query with an outer filtering query:
SELECT * FROM (
SELECT vendor_no As vendor,
factory_no As factory,
COUNT(1) as count,
SUM(CASE WHEN fcr_date > completion_date THEN 1 ELSE 0 END) as filter_orders,
ROUND(filter_orders / count * 100, 4) as percent
FROM #orders
GROUP BY vendor_no,
factory_no
) x
WHERE percent>20
I'm pretty sure you can't use aliases (like percent) in having clauses or group by clauses. And by "pretty" I mean Oracle won't let me use aliases in having/group by clauses...not sure about other vendors.