SQL query that returns a set - sql

I have a following table
socket_id | event_type | response
----------+------------+---------
2 | 2 | 404
----------+------------+---------
2 | 4 | 200
----------+------------+---------
2 | 2 | 500
----------+------------+---------
3 | 2 | 400
----------+------------+---------
2 | 3 | 404
----------+------------+---------
I need an SQL query that returns a set of all sockets with number of different event types registered by each of them, ordered by socket_id ASC
So far I wrote this query by it doesn't return different event_types for same socket_id.
SELECT socket_id,
SUM(CASE WHEN event_type >= 1 THEN 1 ELSE 0 END) types
FROM Events
GROUP BY socket_id;

As if you mean this :
SELECT socket_id,
count(distinct event_type) as types
FROM Events
GROUP BY socket_id
ORDER BY socket_id;

Your description suggests me :
SELECT socket_id, COUNT(event_type) AS types
FROM Events
GROUP BY socket_id
ORDER BY socket_id;
If you want unique event_type count then use DISTINCT inside COUNT().

Related

How do you flip rows into new columns?

I've got a table that looks like this:
player_id | violation
---------------------
1 | A
1 | A
1 | B
2 | C
3 | D
3 | A
And I want to turn it into this, with a bunch of new columns that refer to the types of violations, and then the sum of the number of each individual type of violation that each player got (not that concerned with what the columns are called; a/b/c/d would work great as well):
player_id | violation_a | violation_b | violation_c | violation_d
-----------------------------------------------------------------
1 | 2 | 1 | 0 | 0
2 | 0 | 0 | 1 | 0
3 | 1 | 0 | 0 | 1
I know how I could do this, but it would take a ton of lines of code, since there are in reality 100+ types of violations. Is there any way (perhaps with a tablefunc()?) that I could do this more concisely than spelling out each of the new 100+ columns that I want and the logic for them each individually?
In pure SQL I don't see how you could avoid declaring the columns yourself. You either have to create subselects or filters in every column ..
SELECT DISTINCT ON (t.player_id)
t.player_id,
count(*) FILTER (WHERE violation = 'A') AS violation_a,
count(*) FILTER (WHERE violation = 'B') AS violation_b,
count(*) FILTER (WHERE violation = 'C') AS violation_c,
count(*) FILTER (WHERE violation = 'D') AS violation_d
FROM t
GROUP BY t.player_id;
.. or create a pivot table:
SELECT *
FROM crosstab(
'SELECT player_id, t2.violation, count(*) FILTER (WHERE t.violation = t2.violation)::INT
FROM t,(SELECT DISTINCT violation FROM t) t2
GROUP BY player_id, t2.violation'
) AS ct(player_id INT,violation_a int,violation_b int,violation_c int,violation_d int);
Demo: db<>fiddle

How to aggregate based on various conditions

lets say I have a table which stores itemID, Date and total_shipped over a period of time:
ItemID | Date | Total_shipped
__________________________________
1 | 1/20/2000 | 2
2 | 1/20/2000 | 3
1 | 1/21/2000 | 5
2 | 1/21/2000 | 4
1 | 1/22/2000 | 1
2 | 1/22/2000 | 7
1 | 1/23/2000 | 5
2 | 1/23/2000 | 6
Now I want to aggregate based on several periods of time. For example, I Want to know how many of each item was shipped every two days and in total. So the desired output should look something like:
ItemID | Jan20-Jan21 | Jan22-Jan23 | Jan20-Jan23
_____________________________________________
1 | 7 | 6 | 13
2 | 7 | 13 | 20
How do I do that in the most efficient way
I know I can make three different subqueries but I think there should be a better way. My real data is large and there are several different time periods to be considered i. e. in my real problem I want the shipped items for current_week, last_week, two_weeks_ago, three_weeks_ago, last_month, two_months_ago, three_months_ago so I do not think writing 7 different subqueries would be a good idea.
Here is the general idea of what I can already run but is very expensive for the database
WITH
sq1 as (
SELECT ItemID, sum(Total_shipped) sum1
FROM table
WHERE Date BETWEEN '1/20/2000' and '1/21/2000'
GROUP BY ItemID),
sq2 as (
SELECT ItemID, sum(Total_Shipped) sum2
FROM table
WHERE Date BETWEEN '1/22/2000' and '1/23/2000'
GROUP BY ItemID),
sq3 as(
SELECT ItemID, sum(Total_Shipped) sum3
FROM Table
GROUP BY ItemID)
SELECT ItemID, sq1.sum1, sq2.sum2, sq3.sum3
FROM Table
JOIN sq1 on Table.ItemID = sq1.ItemID
JOIN sq2 on Table.ItemID = sq2.ItemID
JOIN sq3 on Table.ItemID = sq3.ItemID
I dont know why you have tagged this question with multiple database.
Anyway, you can use conditional aggregation as following in oracle:
select
item_id,
sum(case when "date" between date'2000-01-20' and date'2000-01-21' then total_shipped end) as "Jan20-Jan21",
sum(case when "date" between date'2000-01-22' and date'2000-01-23' then total_shipped end) as "Jan22-Jan23",
sum(case when "date" between date'2000-01-20' and date'2000-01-23' then total_shipped end) as "Jan20-Jan23"
from my_table
group by item_id
Cheers!!
Use FILTER:
select
item_id,
sum(total_shipped) filter (where date between '2000-01-20' and '2000-01-21') as "Jan20-Jan21",
sum(total_shipped) filter (where date between '2000-01-22' and '2000-01-23') as "Jan22-Jan23",
sum(total_shipped) filter (where date between '2000-01-20' and '2000-01-23') as "Jan20-Jan23"
from my_table
group by 1
item_id | Jan20-Jan21 | Jan22-Jan23 | Jan20-Jan23
---------+-------------+-------------+-------------
1 | 7 | 6 | 13
2 | 7 | 13 | 20
(2 rows)
Db<>fiddle.

Count specific values that belong to the same column SQL Server

I would like to ask for help I would like to count the number of records but having 2 conditions for 2 specific values only from a one single column:
Count the records first:
count(service) as contacts
service | state | address
service1| 1 |123
service2| 1 |321
service3| 3 |332
service1| 2 |333
service2| 2 |111
service3| 3 |333
1st result
Service | Contacts | status
service1| 1 | 1
service1| 1 | 2
service1| 1 | 3
service2| 1 | 1
service2| 1 | 2
service2| 1 | 3
if status = 1 and 2 then add to count else 0 (only count who's "status" is equal to 1 and 2.
Result:
Final result
Service | Contacts
Service1 | 2
Service2 | 2
sorry for the confusion
Thanks for your big help
This should work for all major databases
SELECT service,
SUM(CASE WHEN status IN(1,2) THEN contacts ELSE 0 END) as Contacts
FROM (your query) as x
GROUP BY service
select service,
sum(case when status in(1,2) then contacts else 0 end) as contact_count
from MyTable
group by service
What this will do is evaluate each row and include the contacts value in the sum where the status is one that is required. As it is an aggregate, you need to group by the service

Selecting only the very last set of rows matching the criteria

Considering following data
test1=# select * from sample order by created_at DESC;
id | status | service | created_at
----+--------+---------+---------------------
8 | OK | 1 | 2015-09-16 11:54:00
7 | OK | 1 | 2015-09-16 11:53:00
6 | FAIL | 1 | 2015-09-16 11:52:00
5 | OK | 1 | 2015-09-16 11:51:00
How can I select only the rows with ID 7 and 8. Using window functions I can get row numbers partitioned over status, but so far did not figure out the way how to limit the results only to the last rows identifying 'successful period' for given service.
You need to find the time of the most recent status = 'FAIL' for each service, then select those records of the same service that are more recent:
SELECT *
FROM sample
LEFT JOIN (
SELECT service, max(created_at) AS last_fail
FROM sample
WHERE status = 'FAIL'
GROUP BY service) f USING (service)
WHERE created_at > last_fail
OR last_fail IS NULL; -- also show services without ever failing
This assumes there are only two status codes. If there are more, add a status = 'OK' filter to the WHERE clause.
The most simple approach would be this:
SELECT *
FROM sample AS s
LEFT JOIN (SELECT service, max(id)
FROM sample
WHERE status = 'FAIL'
GROUP BY service) AS q
ON s.id > q.id
AND s.service = q.service

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)