SQL multiple COUNT - sql

Let's consider this table:
[name] [type]
"Ken Anderson" 1
"John Smith" 2
"Bill Anderson" 1
"George Anderson" 1
"Taylor Smith" 1
"Andrew Anderson" 2
"Dominic Smith" 2
and that query:
SELECT mates.type, COUNT(*) AS SmithsCount
FROM mates
WHERE mates.name LIKE "* Smith"
GROUP BY mates.type
The result should be like
[type] [SmithsCount]
1 1
2 2
What if I want to get also Andersons Count in each group? Like
[type] [SmithsCount] [AndersonsCount]
1 1 3
2 2 1
And, of course, I want this to be most simple as it can be ;) I'm pretty new in SQL, I readed tutorials on W3 Schools and http://www.sql-tutorial.net/ but there are just poorly exampled basics, any "more" complicated queries. Anybody has some useful links? Thanks.

select type,
sum(case when name like '% Smith' then 1 else 0 end) as SmithCount,
sum(case when name like '% Anderson' then 1 else 0 end) as AndersonCount
from mates
group by type

You need a pivot table. This is a feature supported by some RDBMS (Oracle, SQLServer and probably others).
A pivot table let's you use values as columns for aggregations. See my post here: How to transform vertical data into horizontal data with SQL?
The pivot table will alow you to also get the counts of all the other people in your list.

Your query is close, but you must use % instead of * as the wildcard character.
select type,
sum(case when name like '%Smith' then 1 else 0 end) as SmithCount,
sum(case when name like '%Anderson' then 1 else 0 end) as AndersonCount
group by type

In standard SQL parlance this is not supported in the presentation that you propose.
Standard SQL way would be to first normalize data into mates.first_name, mates.last_name and then do:
SELECT mates.type, mates.last_name, COUNT(*) AS last_name_count
FROM mates
WHERE mates.last_name IN ('Smith', 'Anderson')
GROUP BY mates.type, mates.last_name
Which would provide output such as
type last_name last_name_count
1 Anderson 3
1 Smith 1
2 Anderson 1
2 Smith 2
this is the same info that you are looking for, but the format/presentation is not the same.
Historically you were supposed to pivot/crosstab this data in the client application (as part of the presentation layer).
Of course a lot of times it is useful or necessary to do it in SQL layer so extensions to the standard were made, such as pivot (MSSQL) or crosstab (postgres), etc...

Related

force query to produce records for each value in lookup table

I have a lookup table that is 1 to N with a data table. The look up table has the
By example the lookup table contains (Dog, Cat, Bird, Exotic)
The data table has the following fields.
house, animal_type, quantity
If I have data such as
house animal_type quantity
1 dog 1
1 cat 1
2 dog 2
3 exotic 1
How do I get a query that will produce the following? (The order of the column headings is immaterial).
house dog cat bird exotic
1 1 1 0 0
2 2 0 0 0
3 0 0 0 1
I know about
IIF([animals].[quantity] is null,0,[animals].[quantity])
But it is not producing a zero record for each animal type even if it is not at that house.
You can get what you need with a crosstab query. See the Access help topic: TRANSFORM Statement (Microsoft Access SQL). And check out the Access Crosstab Query Wizard to get started.
It seems you want a column for bird even when no house has one. So add IN ('dog', 'cat', 'bird', 'exotic') to the PIVOT clause:
TRANSFORM Sum(data_table.[quantity]) AS SumOfquantity
SELECT data_table.[house]
FROM data_table
GROUP BY data_table.[house]
PIVOT data_table.[animal_type] IN ('dog', 'cat', 'bird', 'exotic');
That query returns Null instead of zero where a house does not have a record for an animal_type. Include Nz() if you prefer zero instead of Null:
TRANSFORM Nz(Sum(data_table.[quantity]), 0) AS SumOfquantity
With that query, Access 2010 gives me this result set from your sample data:
You can use TRANSFORM operator in the MS Access to solve your task.
TRANSFORM Nz(SUM(quantity), 0)
SELECT house FROM Test
GROUP BY house
PIVOT animal_type
Output of this query
More information about it you can find here (https://msdn.microsoft.com/en-us/library/bb208956(v=office.12).aspx)
The following link explains step by step to get a crosstab query to show dynamic headings even if there is not associated data for that heading.
How to Tame the Crosstab Missing Column Beast

Conditioning on multiple rows in a column in Teradata

Suppose I have a table that looks like this:
id attribute
1 football
1 NFL
1 ball
2 football
2 autograph
2 nfl
2 blah
2 NFL
I would like to get a list of distinct ids where the attribute column contains the terms "football", "NFL", and "ball". So 1 would be included, but 2 would not. What's the most elegant/efficient way to do this in Terdata?
The number of attributes can vary for each id, and terms can repeat. For example, NFL appears twice for id 2.
You can use the following:
select id
from yourtable
where attribute in ('football', 'NFL', 'ball')
group by id
having count(distinct attribute) = 3
See SQL Fiddle with Demo (fiddle is showing MySQL, but this should work in TeraData)

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

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.

Best way to count this Data

In short I have 2 tables:
USERS:
------------------------
UserID | Name
------------------------
0 a
1 b
2 c
CALLS:
------------------------
ToUser | Result
------------------------
0 ANSWERED
1 ENGAGED
1 ANSWERED
0 ANSWERED
Etc, etc (i use a numerical referance for result in reality)
I have over 2 million records each detailing a call to a specific client. Currently I'm using Case statements to count each recurance of a particular result AFTER I have already done the quick total count:
COUNT(DISTINCT l_call_log.line_id),
COALESCE (SUM(CASE WHEN l_call_log.line_result = 1 THEN 1 ELSE NULL END), 0) AS [Answered],
COALESCE (SUM(CASE WHEN l_call_log.line_result = 2 THEN 1 ELSE NULL END), 0) AS [Engaged],
COALESCE (SUM(CASE WHEN l_call_log.line_result = 4 THEN 1 ELSE NULL END), 0) AS [Unanswered]
Am I doing 3 scans of the data after my inital total count? if so, is there a way I can do one sweep and count the calls as-per-result in one go?
Thanks.
This would take one full table scan.
EDIT: There's not enough information to answer; because the duplicate removal (DISTINCT) that I missed earlier, we can't tell what strategy that would be used.... especially without knowing the database engine.
In just about every major query engine, each aggregate function is executed per each column per each row, and it may use a cached result (such as COUNT(*) for example).
Is line_result indexed? If so, you could leverage a better query (GROUP BY + COUNT(*) to take advantage of index statistics, though I'm not sure if that's worthwhile depending on your other tables in the query.
There is the GROUP BY construction in SQL. Try:
SELECT COUNT(DISTINCT l_call_log.line_id)
GROUP BY l_call_log.line_result
I would guess it's a table scan, since you don't have any depending subqueries. Run explain on the query to be sure.

How to output a column per group that an item is a member of?

Given this SQL Input Table
GroupId Item
1 Fish
1 FishBowl
2 Fish
3 Fish
How Can i derive this Output?
Item IsInGroup1 IsInGroup2 IsInGroup3
Fish Y Y Y
FishBowl Y N N
Please note that the Number of Groups can be variable
In general, you can't do a variable number of groups with standard SQL. You have to know the groups beforehand. This is because any SQL query must know the number of columns, and their names.
SELECT Item,
MAX(CASE GroupId WHEN 1 THEN 'Y' ELSE 'N' END) AS IsInGroup1,
MAX(CASE GroupId WHEN 2 THEN 'Y' ELSE 'N' END) AS IsInGroup2,
MAX(CASE GroupId WHEN 3 THEN 'Y' ELSE 'N' END) AS IsInGroup3
FROM ThisInputTable
GROUP BY Item;
Microsoft SQL Server does have some facility for PIVOT tables, however this is not standard SQL. I'm not a Microsoft user so I'll give you a link to "Using PIVOT and UNPIVOT," and leave the rest to you.
One of the most frequently asked questions on SO.
Basically, you can use dynamic SQL to build a query of the form Bill Karwin gives by determining the number of groups and a name for each group. Such a query you generate can be ANSI SQL. You can also generate proprietary syntax for SQL Server 2008 (2005 and above)'s PIVOT operator which Bill mentioned.
My example at the first link should be pretty clear.
Well, I suppose it depends on which database you are using. This works for me:
SELECT
Item,
IF(GroupId=1,'Y','N') As IsInGroup1,
IF(GroupId=2,'Y','N') As IsInGroup2,
IF(GroupId=3,'Y','N') As IsInGroup3
FROM
New Table
GROUP BY
Item ;