I have a "library" history table (Excel like) to see where a book was or is.
id
date
book
action
client
1
2020-12-01
1
toClient
1
2
2020-12-02
1
returned
1
3
2020-12-03
1
toClient
2
4
2020-12-04
2
toClient
2
5
2020-12-05
3
toClient
1
6
2020-12-06
3
returned
1
7
2020-12-07
2
returned
2
8
2020-12-08
2
toClient
1
I am trying to discover how many books and what books are by client 2 (for example).
I have something like that to find everything about the books that are or was by client 2:
SELECT * FROM history WHERE book IN
(SELECT book FROM history WHERE action='toClient' AND client=2 GROUP BY book)
But so, I get also books that aren't anymore on client 2.
Is there a simple way to query items where the last action was "toClient" and not "returned" and the current client is client 2?
Edit: forgot to write that action can be also revision, timeExtension or something other.
To list books that are currently out, you can do:
select h.*
from history h
where
action = 'toClient'
and h.date = (select max(d1.date) from history h1 where h1.book = h.book)
The idea is to filter on the latest event date per book. Then we only retain the row if the corresponding event is "toClient".
This returns the results for all clients. If you want just one client, just add an additional condition to the where clause of the query:
and client = 2
You can also do this with window functions:
select *
from (
select h.*,
rank() over(partition by book order by date desc) rn
from history h
) h
where rn = 1 and action = 'toClient' -- and client = 2
Related
accounts as a1 | team_logs as tl1
--------------------------------------------------------
id Name counter | id team_id user_id account_id
1 Account 1 2 | 1 1 100 1
2 Account 2 2 | 2 2 200 1
3 Account 3 0 | 3 3 300 2
... | 4 2 200 2
This is an account review app. Based on the 2 tables above a query is needed that will output 1 account from a1 table based on the tl1 records as below:
A team member is requesting an account, and once an account is assigned to him a log entry is made in tl1 that an account_id is assigned to him.
An account can be assigned to a Team only once.
An account can be assigned to x teams (In the above example we have only 3 teams).
An record can be reviewed x times(In the example above it can be reviewed 3 times).
I had a project where I had only 3 teams and each teams logs were stored in its own table, and I had this query which worked:
Example for Team1
SELECT `a1`.*
FROM `accounts` AS `a1`
LEFT JOIN `team1_logs` AS `tl1` ON tl1.account_id = a1.id
WHERE (tl1.account_id IS NULL)
AND (a1.counter < '3')
ORDER BY RAND()
LIMIT 1
a1 has a counter column which has a value that represents the number of times a row was shown to teams. Now my project can house x teams, we made the teams dynamic, so making a table log for each team isn't an option.
So in the above tables if i want an account to be reviewed(assigned to a team member) 3 times.
Account 1 can be reviewed 1 more time by any team that isn't 1 and 2
Account 2 can be reviewed 1 more time by any team that isn't 2 and 3
What would my new query need to look like if i want to get the next first available record, based on the 1-4 criteria from above?
The data in Table 2 is more than enough, you don't need to know any other
data to make the needed query.
team_id is an query input (since we need to output an account to the team
member)
Answer
Assuming that I am a team member of team 1
SELECT DISTINCT a.*
FROM accounts AS a
LEFT JOIN (
SELECT account_id, team_id FROM team_logs) AS tl1 ON a.id = tl1.account_id
WHERE a.id NOT IN (
SELECT account_id FROM team_logs WHERE team_id =1)
AND a.counter < 3
ORDER BY a.id ASC
If you just want to see which teams are not allowed to review the account again, join with a subquery that uses GROUP_CONCAT to get the list of teams that have reviewed it.
SELECT a.*, 3 - counter AS remaining_reviews, IFNULL(tl.already_reviewed, '') AS already_reviewed
FROM accounts AS a
LEFT JOIN (
SELECT account_id, GROUP_CONCAT(team_id ORDER BY team_id) AS already_reviewed
FROM team_logs
GROUP BY account_id) AS tl ON a.id = tl.account_id
WHERE a.counter < 3
DEMO
Hi there I have a question regarding SQL structure. I have some of it figured out but I cant get the rest.
USERS TABLE
user OS_signup
1 Mobile
2 Windows
3 Mac
4 Mobile
5 Windows
DUNGEON TABLE
user dungeon progress status OS_completed deaths
1 Dungeon 1 1 finished Windows 0
1 Dungeon 1 2 incomplete Windows 0
2 Dungeon 2 1 finished Windows 1
3 Dungeon 2 1 finished Mac 1
3 Dungeon 1 2 finished Mac 3
3 Dungeon 3 3 incomplete Mac 0
4 Dungeon 1 1 finished Mobile 1
5 Dungeon 1 1 incomplete Windows 5
Which operating system is most popular when comparing the percent of users that completed the first level of progress of the first dungeon vs. users who have an account on the game?
I have this query
SELECT OS_signup, count(*) AS count_total
FROM users
GROUP BY OS_signup
and this one
SELECT OS_completed, count(*) AS count_completed
FROM dungeons
WHERE dungeon = "Dungeon 1" AND progress >=1 AND status = "finished"
GROUP BY OS_completed
I obviously get two tables, but I want to write a query that outputs a combined table so that I get one table that looks like this:
OS percent-done
Mobile 0.5
Windows 0.5
Mac 1
How can I write a query to achieve a table like the above?
Thank you!
for MySQL it could be something like this:
SELECT OS_signup, count(*) AS count_total, COUNT(tmp.user_id) as count_completed, COUNT(tmp.user_id) / COUNT(*) as percent
FROM users
LEFT JOIN (
SELECT user_id
FROM dungeons
WHERE dungeon = 'Dungeon 1' AND progress >= 1 AND status = 'finished'
GROUP BY user_id
) tmp ON tmp.user_id = users.id
GROUP BY OS_signup
Just join the results of these two queries together:
WITH total(OS, count_total) AS (
SELECT OS_signup, count(*)
FROM users
GROUP BY OS_signup
),
completed(OS, count_completed) AS (
SELECT OS_completed, count(*)
FROM dungeons
WHERE dungeon = 'Dungeon 1'
AND progress >= 1
AND status = 'finished'
GROUP BY OS_completed
)
SELECT OS,
count_total,
count_completed,
CAST(count_completed AS FLOAT) / count_total AS percent
FROM total
JOIN completed USING (OS)
ORDER BY ...;
(MySQL does not have common table expressions; you'd have to use views or subqueries instead.)
I have in my database 3 tables. One for the Apps, other for the historic of the state of the apps and a last table with the description of each state.
Table Apps:
ID Name
1 App1
2 App2
Table Historic:
ID IDApp IDState DateChanged
1 1 2 2016-06-01
2 1 4 2016-06-07
3 2 1 2016-06-05
4 2 2 2016-06-12
Table State:
ID Description
1 Open
2 Working
3 Pending
4 Closed
I want a query that returns the last state of each App. I want the return like this:
Name Description Date
App1 Closed 2016-06-07
App2 Working 2016-06-12
You should consider making your DateChanged field a date/time field if there can be multiple possible states witnessed for a given app on a single day. As-is this should work but state reporting for a given app for a given day will arbitrarily choose the status with the highest ID if there are > 1 status witnessed for a given app on a the most recent day reported in history.
SELECT a.Name,
COALESCE(s.Description, '(No History)') as Description,
h.DateChanged as Date
FROM Apps a LEFT JOIN (
SELECT IDApp,
MAX(IDState) as IDState, -- arbitrary tie breaker for > 1 state in a day
DateChanged
FROM Historic h1 INNER JOIN (
SELECT IDApp, MAX(DateChanged) as MaxDateChanged
FROM Historic
GROUP BY IDApp
) h2 ON h1.IDApp = h2.IDApp
AND h1.DateChanged = h2.MaxDateChanged
GROUP BY IDApp, DateChanged
) h ON a.ID = h.IDApp
LEFT JOIN State s ON s.ID = h.IDState
SELECT Name, Description, MAX(DateChanged) from Apps
INNER JOIN Historic ON Apps.ID=Historic.IDapp
INNER JOIN State ON State.ID=Historic.IDState
GROUP BY Name, Description
I have the following data stored in my database.
I want to know what the 10 most searched parts are for every car.
Below I made an example of the data that is stored in the database.
One table contains the names of the cars with the car id.One table contains the requests with one or more request id('s) for every car.One table contains the request id with the name of the requested part.
Table cars
audi (7)
bmw (12)
Table request
7 (100)
7 (234)
7 (367)
7 (562)
7 (729)
7 (765)
7 (881)
Table request_parts
100 (achterband)
234 (voorband)
367 (motor)
562 (accu)
729 (achterband)
765 (kopeling)
881 (koeling)
What the query should return is something like this, as in the example 'achterband' was found twice
audi achterband 2
audi voorband 1
audi motor 1
audi accu 1
audi kopeling 1
The query that I currently have counts how often the part 'motor' has been requested for every car. however I can't find out how to do this not just for one product but for all of them at the same time. right now its not important to have the name of the car as the id is already shown.
SELECT COUNT(*), requests.sibben_brand_id, request_parts.name
FROM request_parts
JOIN requests ON requests.id = request_parts.request_id
WHERE requests.sibben_brand_id IS NOT NULL
AND request_parts.name LIKE 'motor'
GROUP BY requests.sibben_brand_id
ORDER BY COUNT(*) DESC `
Does any one has a idea how i could get the correct data?
try with this:
SELECT COUNT(*), requests.sibben_brand_id, request_parts.name
FROM request_parts
JOIN requests ON requests.id = request_parts.request_id
WHERE requests.sibben_brand_id IS NOT NULL
GROUP BY requests.sibben_brand_id,request_parts.name
ORDER BY COUNT(*) DESC `
with
cars table as (id,name)
request table as (id,car_id,request_id)
request_parts table as (id,request_id,part_name)`
the following query returns top 10 records
select top 10 c.name, rqp.part_name,count(*) as repetition
from request as r, request_parts as rqp, cars as c
where rqp.request_id = r.request_id AND r.car_id = c.Id
group by rqp.part_name,c.name
order by repetition desc`
I have the following table structure for notifications table.
id user_id post_id type status date seconduser_id
1 1 23 1 0 somedate 4
2 2 25 2 0 somedate 3
3 3 26 1 0 somedate 4
4 4 28 2 1 somedate 5
5 5 21 2 0 somedate 4
---
---
and so on
Here type = 1 means a like and type = 2 means a comment. status = 0 means seconduser_id hasn't seen the notification yet. seconduser_id is the notification recipient.
Is it possible to get notifications for 1 user (example seconduser_id = 4), with notification grouped by type, showing count and the latest user_id for each type (in one query)?
The implementation of this would be something like User3 and 10 other people liked your post.
Edit: So far I have something that pulls all notification for user 4 and then groups in php. I don't think this is efficient and so am looking for better solutions.
The basic answer is an aggregation query. The complication is getting the latest user id for each type. THere is one method, using substirng_index()/group_concat():
select type, count(*),
substring_index(group_concat(user_id order by id desc), ',', 1) as latest_user
from notifications n
where secondaryuser_id = 4 and status = 0
group by type;
I am not sure if you also want to filter by status.
Edit (added by OP):
Using the above code and grouping by both post_id and type. Because you want to say User1 and 10 others liked your post. Which means for each grouped notification, post_id has to be unique.
SELECT *, substring_index(group_concat(user_id order by id desc), ',', 1) as latest_user, COUNT(post_id) AS total
FROM notifications n
WHERE seconduser_id = 4 and status = 0
GROUP BY post_id, type
Try this:
SELECT MAX(user_id) AS LatestUser, type, COUNT(type) AS total
FROM notifications
WHERE seconduser_id = 4
GROUP BY type