SQL-Server - Ability to pass subset parameter in the select? - sql

I'm trying create a query that will output a total number, as well as a subset of the total number in SQL-Server. I can think of a way to do this via subqueries, but that seems like a ton of work. Is there a faster way to write this query?
Table name: orders
OrderID Date Type OrderSize
1 1/1/2012 Electronics $282.02
2 1/1/2012 Electronics $1,000.56
3 1/1/2012 Books $17.25
4 1/1/2012 Books $10.00
What I am trying to output would look like this:
Date ElectronicOrders ElectronicOrderSize BookOrders BookOrderSize
1/1/2012 2 $1,282.58 2 $27.25
I could create a temp table, then run 2 update queries - 1 WHERE Type = 'Electronics' and 1 WHERE Type = 'Books'.
What I have seen in some programming languages, such as R, is the ability to subset a variable. Is there a way for me to say something like:
count(OrderID, Type = 'Electronics) as ElectronicOrders, sum(OrderSize, Type = 'Electronics') as ElectronicOrderSize
Or am I stuck with subqueries and UPDATE queries?

I haven't ever gotten the new PIVOT syntax to make sense in my head but you can do a pivot table by grouping, and taking aggregate functions in a case statement.
select [date], sum( case when type = 'Electronics' then (ordersize) else 0 end) AS ElectronicsSum,
sum( case when type = 'Electronics' then 1 else 0 end) AS ElectronicsCount,
sum( case when type = 'Books' then (ordersize) else 0 end) AS BooksSum,
sum( case when type = 'Books' then 1 else 0 end) AS BooksCoumt
from orders
group by [date]
I put a fiddle thing up to test it out. If Aaron B. posts up a solution, give him the answer credit, I might not have even recognized the pivotyness of it.

Related

Pivot aggregates in SQL

I'm trying to find a way to pivot the table below (I guess you would say it's in "long" format) into the ("wider") format where all the columns are essentially explicitly Boolean. I hope this simple example gets across what I'm trying to do.
Note there is about 74 people. (so the output table will have 223 columns, 1 + 74 x 3 )
I can't figure out an easy way to do it other than horribly with a huge number of left joins along "Town" by statements like
... left join(
select
town,
case where person = 'Richard' then 1 else 0 end as "Richard"
Fee as "Richard Fee"
from services
where person = 'Richard'
left join...
can some smart person suggest a way to do this using PIVOT functions in SQL?
I am using Snowflake (and dbt so I can get some jinja into play if really necessary to loop through all the people).
Input:
Desired output:
ps. I know this is a ridiculous SQL ask, but this is the "output the client wants" so I have this undesirable task to fulfil.
If persons are known in advance then you could use conditional aggregation:
SELECT town,
MAX(CASE WHEN person = 'Richard' THEN 1 ELSE 0 END) AS "Richard",
MAX(CASE WHEN person = 'Richard' THEN Fee END) AS "Richard Fee",
MAX(CASE WHEN person = 'Richard' THEN Service END) AS "Richard Service",
MAX(CASE WHEN person = 'Caitlin' THEN 1 ELSE 0 END) AS "Caitlin",
...
FROM services
GROUP BY town;

How to count when don't know specific types

I'm working on a table kind of like this:
id
type
1
A
2
A
3
B
4
C
5
C
I wanted to count the number of ids for each type, and get a table like this.
type_a
type_b
type_c
2
1
2
What I did was
SELECT
SUM(CASE WHEN type = 'A' THEN 1 ELSE 0 END) AS type_a,
SUM(CASE WHEN type = 'B' THEN 1 ELSE 0 END) AS type_b,
SUM(CASE WHEN type = 'C' THEN 1 ELSE 0 END) AS type_c
FROM myTable
My question is, if I don't know how many types are there, and can't specificly list all cases, how can I achieve it?
You are looking for "cross tabulation" or a "pivot table". I added tags.
However:
if I don't know how many types are there, and can't specifically list all cases, how can I achieve it?
Basically, that's impossible in a single SQL query because SQL demands to know the number of result columns at call time. It cannot return a dynamic number of columns on principle.
There are various workarounds with polymorphic types, or with a document type like json, jsonb, hstore or xml, or return arrays instead of individual columns ...
But to get exactly what you are asking for, an unknown number of dedicated columns, you need a two-step workflow. Like:
Build the query dynamically (determining the return type).
Execute it.
Related:
Dynamic alternative to pivot with CASE and GROUP BY
PostgreSQL Crosstab Query
That said, if your case is simple and you deal with a hand full of known types, you can just over-provision. With a faster crosstab() query, or with simple conditional aggregation like you have it, just more elegant and efficient with the aggregate FILTER clause:
SELECT count(*) FILTER (WHERE type = 'A') AS type_a
, count(*) FILTER (WHERE type = 'B') AS type_b
, count(*) FILTER (WHERE type = 'C') AS type_c
, count(*) FILTER (WHERE type = 'D') AS type_d
-- that's all folks!
FROM tbl;
Types with no entries report 0 (count() never returns NULL) which would be correct anyway.
Does not work for unknown types, obviously.
If I understand you correctly, all you want is a simple COUNT:
SELECT
type
,COUNT(id)
FROM myTable
GROUP BY type

replace value with 1 and apply SUM method in one query SQL

I need to replace the value in column imp from 1-0 to 1 and sum it up. Column imp is a STRING, so it also needs to be converted to INT. Each line represents the record so I need to sum up imp and group by another column (in order to get the number of records for a specific Advertiser)
My data looks like this:
advertiser advertiser_id imp
Frank 123 1-0
Frank 123 1-0
Mike 124 1-0
My query:
SELECT
a.AdvertiserID as AdvertiserID,
SUM(
CASE
WHEN a.imp = '1-0' THEN "1"
END
),
b.advertiser_name as AdvertiserName,
FROM
(
`bigquery_table_a` a
INNER JOIN `another_bigquery_table` b ON (a.AdvertiserID = b.advertiser_id)
)
GROUP BY
AdvertiserID,
AdvertiserName
Error message: No matching signature for aggregate function SUM for argument types: STRING
Desired output:
advertiser advertiser_id imp
Frank 123 2
Mike 124 1
Are you just looking for aggregation and count()?
SELECT AdvertiserID, AdvertiserName, COUNT(*)
FROM `bigquery_table_a` a JOIN
`another_bigquery_table` b
ON (a.AdvertiserID = b.advertiser_id
GROUP BY AdvertiserID, AdvertiserName;
Or, if you specifically want to count values of '1-0', use COUNTIF():
SELECT AdvertiserID, AdvertiserName, COUNTIF(imp = '1-0')
FROM `bigquery_table_a` a JOIN
`another_bigquery_table` b
ON (a.AdvertiserID = b.advertiser_id
GROUP BY AdvertiserID, AdvertiserName;
The immediate problem with your code is that as you point out you want an INTEGER type so you can sum it;. Yet your CASE goes out of its way to return a STRING type instead. So you could change
CASE
WHEN a.imp = '1-0' THEN "1"
END
to
CASE
WHEN a.imp = '1-0' THEN 1
END
You might also want to add an ELSE condition, but honestly that isn't likely to matter for your purpose; if you did, it would look like
CASE
WHEN a.imp = '1-0' THEN 1
ELSE 0
END
Now there also may be simpler ways to get the count you want; what else you might do depends what DBMS you're using. But from where you are, the above seems like the simplest fix.

Count where conditions are different

I've been trying to optimize one of my more bulky db views.
Presently, I'm just using sub-selects 5 times to get the count of the company ID's.
(Select count(id) from company table where prospecting.stage = 'qualify') as Qualify,
(Select count(id) from company table where prospecting.stage = 'targetted') as Targetted,
Each company goes through 5 stages, I simply want to count the amount of companies in each stage by company location in separate columns.
I'm trying to do this in one select, but I am getting a bit stuck.
SUM(COUNT(CASE WHEN prospecting.stage = 'Qualify' THEN '1' ELSE '0' END)) as [Qualified]
SUM(COUNT(CASE WHEN prospecting.stage = 'Targetted' THEN '1' ELSE '0' END)) as [Targetted]
So it ends up looking something along these lines:
Location | Stage: Qualify | Stage: Targetted | Stage 3 | Stage 4 | Stage 5 | Total
Cannot perform an aggregate function on an expression containing an aggregate or a subquery. -Makes sense.
So I need to count the Company.ID where the prospecting.stage = 'XYZ' into separate rows per stage.
Any advice? :(
Drop the count function and change the datatype from char to int in the case expressions. Your expressions should look like this:
SUM(CASE WHEN prospecting.stage = 'Qualify' THEN 1 ELSE 0 END) as [Qualified]

select sum(a), sum(b where c=1) from db; sql conditions in select statement

i guess i just lack the keywords to search, but this is burning on my mind:
how can i add a condition to the sum-function in the select-statement like
select sum(a), sum(b where c=1) from db;?
this means, i want to see the sum of column a and the sum of column b, but only of the records in column b of which column c has the value 1.
the output of heidi just says "bad syntac near WHERE". may there be any other way?
thanks in advance and best regards from Berlin, joachim
The exact syntax may differ depending on the database engine, however it will be along the lines of
SELECT
sum(a),
sum(CASE WHEN c = 1 THEN b ELSE 0 END)
FROM
db
select sum(case when c=1 then b else 0 end)
This technique is useful when you need a lot of aggregates on the same set of data - you can query the entire table without applying a where filter, and have a bunch of these which give you aggregated data for a specific filter.
It's also useful when you need a lot of counts based on filters - you can do sums of 1 or 0:
select sum(case when {somecondition} then 1 else 0 end)