sql Properly grouping my table - sql

I'm using MS Access in order to play around with tables through SQL. I want to properly group my table and this is an example of what I want to do. Say I have a table like this:
Cool? | Age
Yes | 15
No | 34
No | 12
Yes | 26
Yes | 10
What I want is the resulting table to show how many ppl are cool or not grouped by age. For instance in this example it would be:
AGE | Count that are cool | Count that is Not cool
<25 | 2 | 1
>=25 | 1 | 1
Thanks in advance!

Try this:
case when age<25 then '<25' when age>=25 then '>=25' end as age, count(case when age<25 then 1 else null end) as [Count that are cool], count(case when age>=25 then 1 else null end) as [Count that is Not cool]
from Table1
group by case when age<25 then '<25' when age>=25 then '>=25' end

Related

Alternative to CASE Statement to Compare and Summarize Data

I have a database that looks like the following;
--------------------------------
region | price_a | price_b
--------------------------------
USA | 100 | 120
USA | 150 | 150
Canada | 300 | 300
Mexico | 20 | 25
I need to compare the values from each price column and count the matched and mismatched prices and summarize as follows:
Required Results
--------------------------------
region | price_match | price_mismatch
--------------------------------
USA | 1 | 1
Canada | 1 | 0
Mexico | 0 | 1
I can do this via multiple case statements (below) but I'm wondering if there is a better approach.
Current Code:
SELECT
region,
COUNT(CASE WHEN price_a = price_b THEN 'match' END) AS price_match,
COUNT(CASE WHEN price_a != price_b THEN 'match' END) AS price_mismatch
FROM
FOO
GROUP BY region;
I am guessing from your recent questions, you're using Snowflake, in which case you can use a more compact syntax. I still think using case expression is better from a documentation and portability standpoint;
select region,
sum(iff(price_a=price_b,1,0)) price_match,
sum(iff(price_a=price_b,0,1)) price_mismatch
from cte
group by region;
You can use sum:
select region, sum(price_a = price_b), sum(price_a != price_b)
from foo
group by region

How to create 2 columns using data from 1 column and merging them

I'm facing the some problems in big query, the single column could not separate into 2 columns. I want the column index with 8 and 10 to be new columns called universal_id and project_id using the value in the column "value".
My current table is:
user_id | index | value
a. | 1. | 123
b. | 8. | 456
c. | 10. | 12.60
b. | 10. | 789
I want the result to be this:
user_id | project_id | universal_id |
a | NA | NA
b. | 789 | 456
c. | 12.60 | NA
I have tried this, but it does not work. I searched a lot of places, and could find the answer I am looking for. Any help would be greatly appreciated. Thank you in advance!!!
select user_id,
case when index = 8 then value else null end as universal_id,
case when index = 10 then value else null end as ps_project_id
from test_1
You may use conditional aggregation here:
SELECT
user_id,
MAX(CASE WHEN index = 10 THEN value END) AS project_id,
MAX(CASE WHEN index = 8 THEN value END) AS universal_id
FROM test_1
GROUP BY user_id;
Consider below approach
select * from your_table
pivot (
min(value) for case index
when 10 then 'project_id'
when 8 then 'universal_id'
end in ('project_id', 'universal_id')
)
if applied to sample data in your question - output is

How Can I Count the Number of Times that Different Values Occur in a Column if the Possible Values Are Not Known?

Given the table of items
id | lccnumber | libraryid
--------------------------------------+------------------------+--------------------------------------
d6f7c1ba-a237-465e-94ed-f37e91bc64bd | PR6056.I4588 | 5d78803e-ca04-4b4a-aeae-2c63b924518b
1714f71f-b845-444b-a79e-a577487a6f7d | RC60 .A5 | 5d78803e-ca04-4b4a-aeae-2c63b924518b
1b6d3338-186e-4e35-9e75-1b886b0da53e | PR6056.I4588 | 5d78803e-ca04-4b4a-aeae-2c63b924518b
4428a37c-8bae-4f0d-865d-970d83d5ad55 | PR6056.I4588 | c2549bb4-19c7-4fcc-8b52-39e612fb7dbe
7212ba6a-8dcf-45a1-be9a-ffaa847c4423 | TK5105.88815 .A58 2004 | 5d78803e-ca04-4b4a-aeae-2c63b924518b
100d10bf-2f06-4aa0-be15-0b95b2d9f9e3 | TK5105.88815 .A58 2004 | c2549bb4-19c7-4fcc-8b52-39e612fb7dbe
is there a SQL query that will produce the result set
lccnumber | 5d78803e-ca04-4b4a-aeae-2c63b924518b | c2549bb4-19c7-4fcc-8b52-39e612fb7dbe
------------------------+--------------------------------------+--------------------------------------
PR6056.I4588 | 2 | 1
RC60 .A5 | 1 | 0
TK5105.88815 .A58 2004 | 1 | 1
If the possible libraryids are known ahead of time, then I could do something like
SELECT lccNumber,
SUM(CASE WHEN libraryId = 5d78803e-ca04-4b4a-aeae-2c63b924518b THEN 1 ELSE 0) AS 5d78803e-ca04-4b4a-aeae-2c63b924518b,
SUM(CASE WHEN libraryId = c2549bb4-19c7-4fcc-8b52-39e612fb7dbe THEN 1 ELSE 0) AS c2549bb4-19c7-4fcc-8b52-39e612fb7dbe
FROM items
GROUP BY lccNumber;
but I am looking for a solution in the case that they are not known ahead of time. One approach that would probably work is to first query for the possible libraryIds and then programmatically construct a SELECT clause that accounts for all of these values, but I am wondering if there is a simpler or more efficient way to accomplish it.
You can put the values on separate rows:
SELECT lccNumber, libraryId, COUNT(*)
FROM items
GROUP BY lccNumber, libraryId;
Then re-arrange them at the application layer. You can combine these into records and aggregate into an array for each lccNumber:
SELECT lccNumber, ARRAY_AGG( (libraryId, cnt) )
FROM (SELECT lccNumber, libraryId, COUNT(*) as cnt
FROM items
GROUP BY lccNumber, libraryId
) l
GROUP BY lccNumber

Grouping but with keeping all non-NULL values

Let's say I have the table:
ID | Name | Intolerance
1 | Amy | Lactose
2 | Brian | Lactose
3 | Amy | Gluten
And I run this SQL query:
SELECT
Name,
CASE
WHEN Intolerance = 'Lactose' 1
END AS Lactose,
CASE
WHEN Intolerance = 'Gluten' 1
END AS Gluten
FROM
Table
I get:
Name | Lactose | Gluten
-------+---------+--------
Amy | 1 |
Amy | | 1
Brian | 1 |
But if I try to add "GROUP BY Name", Amy won't have a 1 in both columns, because GROUP BY only selects the last row of each Name. What I want to get instead is this:
Name | Lactose | Gluten
------+---------+---------
Amy | 1 | 1
Brian | 1 |
How can I get that? Is there perhaps a more efficient way to summarize who's allergic to what from the same input? Thanks in advance.
When using a GROUP BY then the aggregate functions can be used for columns that aren't in the GROUP BY.
In this case I assume you want to use MAX, to get only a 1 or a NULL.
SUM or COUNT can also be used to surround a CASE WHEN.
But then those would return a total.
SELECT
Name,
MAX(CASE WHEN Intolerance = 'Lactose' THEN 1 END) AS Lactose,
MAX(CASE WHEN Intolerance = 'Gluten' THEN 1 END) AS Gluten
FROM Table
GROUP BY Name
ORDER BY Name
Or if you don't want to see NULL's?
Then let the CASE return a varchar instead of a number.
SELECT
Name,
MAX(CASE WHEN Intolerance = 'Lactose' THEN '1' ELSE '' END) AS Lactose,
MAX(CASE WHEN Intolerance = 'Gluten' THEN '1' ELSE '' END) AS Gluten
FROM Table
GROUP BY Name
ORDER BY Name
I think what you need is the sum of the number of number of intolerances for each person. Also, put a ELSE so the value is 0 or 1:
SELECT
Name,
SUM(CASE WHEN Intolerance = 'Lactose' THEN 1 ELSE 0 END) AS Lactose,
SUM(CASE WHEN Intolerance = 'Gluten' THEN 1 ELSE 0 END) AS Gluten
FROM Table
GROUP BY Name
ORDER BY Name
I feel that each time I encounter a question like that, it's because a no proper amount of thinking on design was allowed to the project.
To put it simply : you are trying to move data to columns. This is what your application layer is for ! Not the database. People tend to mix what databases are for with what application / UI layer and vice versa are for !
And each time it happens, I see people reaping their mind to answer because that's the point here : answer the question no matter what. Don't question what the OP want to do, give him the answer...
Sorry for that, I am just a little bit pissed.
My solution :
Keep your original query and do the aesthetic on your UI / application layer side. You probably have a IList inside each Person. Just fill them and give the UI the opportunity to display them however it wants.
Because that's what you're asking the database to do : aesthetics.

How to retrieve data from different rows of the same table based on different criteria

I'm trying to write a plain SQL statement for building an Oracle report but I'm stuck at some point. x_request table stores the requests made and different tasks related to specific requests that have been done are stored in x_request_work_log. To summarize the structure of these tables:
X_request
-id
-name
-requester
-request_date
x_request_work_log
-id
-request_id (foreign key)
-taskId
-start_date
-end_date
Now let's assume that these tables are filled with sample data as follows:
x_request
id name requester request_date
1 firstReq John 01/01/2012
2 secondReq Steve 21/01/2012
x_request_work_log
id requestId taskId startDate endDate
1 1 0 01/01/2012 03/01/2012
2 1 1 04/01/2012 04/01/2012
3 1 2 05/01/2012 15/01/2012
4 2 0 24/01/2012 02/02/2012
The template of my report is as follows:
requestName timeSpent(task(0)) timeSpent(task(1)) timeSpent(task(2))
| | | | | | | |
So, that's where I'm stuck. I need a Sql Select statement that will return each row in the formatted way as described above. How can i retrieve and display the start and end dates of different tasks. Btw timeSpent = endDate(task(x)) - startDate(task(x))
Note: Using different select subqueries for each spent time calculation is not an option due to performance constraints. There must be another way.
It sounds like you just want something like
SELECT r.name request_name,
SUM( (CASE WHEN l.taskId = 0
THEN l.endDate - l.StartDate
ELSE 0
END) ) task0_time_spent,
SUM( (CASE WHEN l.taskId = 1
THEN l.endDate - l.StartDate
ELSE 0
END) ) task1_time_spent,
SUM( (CASE WHEN l.taskId = 2
THEN l.endDate - l.StartDate
ELSE 0
END) ) task2_time_spent
FROM x_request_work_log l
JOIN x_request r ON (l.requestId = r.Id)
GROUP BY r.name
If you happen to be using 11g, you could also use the PIVOT operator.
If you need to display all members of a group in one row, you can accomplish this in MySQL with the GROUP_CONCAT operator (I don't know what the equivalent is in Oracle):
> SELECT requestID,
GROUP_CONCAT(DATEDIFF(endDate,startDate)) AS length
FROM request_work_log
GROUP BY requestId;
+-----------+--------+
| requestID | length |
+-----------+--------+
| 1 | 2,0,10 |
| 2 | 9 |
+-----------+--------+
(and then add in the inner join to your other table to replace requestID with the request name)