I need your help. I try to develop monitoring of messages from devices and face some problems with SQL query. I have DB with 4 tables: devices - messages - levels - actions.
devices: id, name;
actions: id, name;
levels: id, action_id, msgcount;
messages: id, action_id, device_id;
Idea is that each devices sending messages about different actions. This messages registered in 'messages' table. Each action has different amount of levels with count of messages to get this level. I want to count registered messages and show in UI progress to next level and som additioonal info. I use following query:
select
mpd.action_id,
mpd.total,
lvl.id as lvl_id,
lvl.msgcount purpose,
lvl.name as level_name,
act.name as action
from
(select
mes.action_id,
count(1) total
from
messages mes,
devices dev
where
mes.device_id=dev.id
and dev.id=5
and mes.action_id not in(select
t.action_id
from
messages t
where
mes.device_id=t.device_id
and t.date > CURDATE())
group by
mes.action_id) mpd,
actions act,
levels lvl
where
mpd.action_id = act.id
and mpd.action_id = lvl.action_id
and lvl.msgcount = (SELECT
MIN(bad.msgcount)
FROM
levels lv
WHERE
lv.msgcount > mpd.total
and lv.action_id = mpd.action_id)
*mpd - messages pro device
But problem is that if top level already recieved this action no more shown in the list. But in this case I want to show the last recieved level(max) and total count of messages. Could someone please help me.
And also I will be very appreciated if you give some advices how to imrove my query.
devices
|------|---------|
| id | name |
|------|---------|
| 3 | RH-SW-12|
| 5 | HRS-PR |
| 6 | PRS-PR |
|------|---------|
levels
|------|-----------|----------|--------|
| id | action_id | msgcount | name |
|------|-----------|----------|--------|
| 1 | 42 | 3 | low |
| 2 | 51 | 3 | start |
| 3 | 51 | 7 | medium |
| 4 | 51 | 15 | hight |
|------|-----------|----------|--------|
actions
|------|--------------|
| id | name |
|------|--------------|
| 42 | connection |
| 51 | stop service |
|------|--------------|
messages
|------|-------------|------------|----------------|
| id | action_id | device_id | date-time |
|------|-------------|------------|----------------|
| 1 | 42 | 3 |14.09.2017 08:51|
| 2 | 42 | 5 |14.09.2017 13:08|
| 3 | 42 | 5 |14.09.2017 16:30|
| 4 | 42 | 5 |15.09.2017 07:43|
| 5 | 51 | 3 |15.09.2017 07:50|
| 6 | 51 | 3 |15.09.2017 10:22|
| 7 | 51 | 3 |15.09.2017 15:11|
| 8 | 51 | 3 |15.09.2017 18:48|
| 9 | 51 | 3 |15.09.2017 19:03|
| 10 | 51 | 5 |15.09.2017 19:18|
| 11 | 42 | 5 |15.09.2017 21:33|
|------|-------------|------------|----------------|
My query now will show following result for device 5:
|------------|---------|----------|-----------|--------------|--------------|
| action_id | total | lvl_id | purpose | level_name | action |
|------------|---------|----------|-----------|--------------|--------------|
| 51 | 1 | 2 | 3 | start | stop service |
|------------|---------|----------|-----------|--------------|--------------|
there is no info about action 42 represented because it has no more levels. Last level was "low" and it was reached.
I want modify the query to get in this case the next result for the device 5:
|------------|---------|----------|-----------|--------------|--------------|
| action_id | total | lvl_id | purpose | level_name | action |
|------------|---------|----------|-----------|--------------|--------------|
| 42 | 4 | 1 | 3 | low | connection |
| 51 | 1 | 2 | 3 | start | stop service |
|------------|---------|----------|-----------|--------------|--------------|
I hope it's possible :)
Try this. This will give you the current levels.
SELECT
*
FROM
devices d,
actions a,
(SELECT
device_id, action_id, COUNT(1) AS count
FROM
messages
GROUP BY device_id , action_id) m,
levels l
WHERE
d.id = m.device_id
AND a.id = m.action_id
AND l.action_id = a.id
AND m.count >= l.msgcount
AND d.id = 5;
Sometimes discussion with right people helps to take a look at the problem from another side. During trying different variant with answer from #HatimStovewala I found right solution:
SELECT
*
FROM
(SELECT
device_id, action_id, COUNT(1) AS count
FROM
messages
WHERE
device_id = 5
GROUP BY device_id , action_id) tmp,
levels lvl
WHERE
lvl.action_id = tmp.action_id
and lvl.msgcount = (SELECT
CASE COALESCE(MIN(msgcount),0) WHEN 0 THEN (SELECT MAX(msgcount) FROM levels WHERE action_id = tmp.action_id) ELSE MIN(repetitions) END AS rep
FROM
levels
WHERE
action_id=tmp.action_id
and msgcount >= tmp.count)
Thank you, Hatim!
Related
I have two tables in a database that I would like to combine in a specific way.
Here are the tables:
table: watchhistory
customerid | titleid | rating | date
------------+-----------+--------+------------
1488844 | tt0389605 | 3 | 2005-09-06
1181550 | tt0389605 | 3 | 2004-02-01
1227322 | tt0389605 | 4 | 2004-02-06
786312 | tt0389605 | 3 | 2004-11-16
525356 | tt0389605 | 2 | 2004-07-11
1009622 | tt0389605 | 1 | 2005-01-19
table: media
mediaid | directorid | title | genre | runtime | releasedate
-----------+------------+----------------+----------------------+---------+-------------
tt0090557 | nm0851724 | Round Midnight | [Drama, Music] | 133 | 1986
tt0312296 | nm0146385 | 1 Giant Leap | [Documentary, Music] | 155 | 2002
tt0078721 | nm0001175 | 10 | [Comedy, Romance] | 122 | 1979
tt2170245 | nm3593080 | 10 | [Thriller] | 76 | 2012
tt5282238 | nm6207118 | 10 | [Thriller] | 90 | 2015
tt0312297 | nm0302572 | 10 Attitudes | [Comedy, Drama] | 87 | 2001
I would like to make a table with the following columns:
title (from media) | Views#
I created this query to get the top 10 titleids, meaning the top 10 titles from watchhistory that appear in watchhistory the most times:
SELECT titleid, count(*) as Views FROM watchhistory GROUP BY titleid ORDER BY Views DESC limit 10;
titleid | views
------------+-------
tt7631348 | 1307
tt14627576 | 1065
tt8372506 | 1063
tt5793632 | 1056
tt1403008 | 1053
tt7825602 | 1051
tt6840954 | 1046
tt12780424 | 1042
tt7266106 | 1036
tt6539274 | 1035
The goal is to essentially replace this titleid column (from watchhistory) with the title (from media). I tried using joins between the watchhistory.titleid and media.mediaid with no luck.
What SQL query do I need to get this desired table?
Thanks in advance.
You need to INNER JOIN to your media table on mediaid:
SELECT m.title, count(wh.*) as Views
FROM watchhistory wh
INNER JOIN media m on m.mediaid = wh.titleid
GROUP BY m.mediaid
ORDER BY Views DESC LIMIT 10;
To see what the select and join are doing, you can simplify it:
SELECT m.*, wh.*
FROM watchhistory wh
INNER JOIN media m on m.mediaid = wh.titleid
The result will be a joined 'table' that has the two tables combined on the mediaid/titleid.
So my example table is like this -
I have a mysql version 5.7 database which I can connect to. Read-only rights.
My table goes like this:
human_id | dog_id | dog_bought_at | amount_paid_for_dog | purchase_place | buyer_has_criminal_past
1 | 1 | 27-12-2019 | 100 | Tokyo | 0
1 | 2 | 03-01-2020 | 200 | Moscow | 0
2 | 3 | 03-01-2020 | 200 | Los Angeles | 0
3 | 4 | 03-01-2020 | 50 | Washington | 0
3 | 3 | 05-01-2020 | 30 | Dallas | 0
4 | 2 | 06-01-2020 | 150 | Texas | 1
What I need to show is this:
dog_id | last_owner_id | total_amount_paid_for_dog | last_purchase_date | last_purchase_place
1 | 1 | 100 | 27-12-2019 | Tokyo
2 | 4 | 350 | 06-01-2020 | Moscow
3 | 3 | 230 | 05-01-2020 | Dallas
4 | 3 | 50 | 03-01-2020 | Washington
Last_purchase_place is shown only for those humans, which do not have criminal past.
what I have tried:
SELECT
e.dog_id
,MAX(e.human_id) last_owner_id
,SUM(e.amount_paid_for_dog) total_amount_paid_for_dog
,MAX(e.dog_bought_at) last_purchase_date
,e_filter.purchase_place last_purchase_place
FROM example e
LEFT JOIN (
SELECT
dog_id
,dog_bought_at
,purchase_place
,human_id
FROM example
WHERE buyer_has_criminal_past != 1
) e_filter ON e.dog_id = e_filter.dog_id AND e.dog_bought_at = e_filter.dog_bought_at
But I am stuck on the logic, that allows to sum up ALL amounts, yet filter out unneeded values.
This is my first question here, so if this is a duplicate or not well written, please say it. Any help appreciated.
SELECT e1.dog_id,
e1.human_id last_owner_id,
sq1.total_amount_paid_for_dog,
e1.dog_bought_at last_purchase_date,
e2.purchase_place last_purchase_place
FROM example e1
JOIN ( SELECT dog_id,
MAX(dog_bought_at) dog_bought_at,
SUM(amount_paid_for_dog) total_amount_paid_for_dog
FROM example
GROUP BY dog_id ) sq1 ON e1.dog_id = sq1.dog_id
AND e1.dog_bought_at = sq1.dog_bought_at
LEFT JOIN example e2 ON e1.dog_id = e2.dog_id
JOIN ( SELECT dog_id,
MAX(dog_bought_at) dog_bought_at
FROM example
WHERE buyer_has_criminal_past = 0
GROUP BY dog_id ) sq2 ON e2.dog_id = sq2.dog_id
AND e2.dog_bought_at = sq2.dog_bought_at
fiddle
I have two queries that pull data from two different tables, but I need them to pull in the same report. I have a shared key between them, and the first table has one entry that corresponds to many entries in the second table.
My first query:
SELECT Proposal_ID,
substr(Proposal_Name, 1, 3) AS Prefix,
substr(Proposal_Name, 4, 6) AS `Number`,
Institution,
CollegeCode,
DepartmentCode,
Proposer_FirstName,
Proposer_LastName
FROM proposals.proposal
WHERE Institution = 'T';
Sample Data:
+----+--------+--------+-------+----------+----------+-----------+----------+
| ID | Prefix | Number | Inst. | CollCode | DeptCode | FirstName | LastName |
+----+--------+--------+-------+----------+----------+-----------+----------+
| 18 | SYP | 4675 | T | AS | SOC | Linda | McGaff |
+----+--------+--------+-------+----------+----------+-----------+----------+
| 20 | GEO | 4340 | T | AS | SGS | Teddy | Graham |
+----+--------+--------+-------+----------+----------+-----------+----------+
My second query:
SELECT Parent_Proposal,
SUBSTRING_INDEX(GROUP_CONCAT(`status`.`Status_Code` ORDER BY `status`.`Status_Time` DESC), ',', 1) AS status_code,
SUBSTRING_INDEX(GROUP_CONCAT(`status`.`Status_Time` ORDER BY `status`.`Status_Time` DESC), ',', 1) AS status_timestamp
FROM proposals.`status`
GROUP BY `status`.Parent_Proposal
Sample Data:
+-----------------+-------------+----------------------+
| Parent_Proposal | Status_Code | Status_Time |
+-----------------+-------------+----------------------+
| 18 | 40 | 2016-11-09 06:30:35 |
+-----------------+-------------+----------------------+
| 20 | 11 | 2017-03-20 10:26:31 |
+-----------------+-------------+----------------------+
I basically need to pull the most recent Status_Code and Status_Timestamp based on the Status_Timestamp and then relate that to the first table with the Parent_Proposal column.
Is there a way to group a subset of results without grouping all of the data together?
Expected Result:
+----+--------+--------+-------+----------+----------+-------+--------+-------------+----------------------+
| ID | Prefix | Number | Inst. | CollCode | DeptCode | FName | LName | Status_Code | Status_Time |
+----+--------+--------+-------+----------+----------+-------+--------+-------------+----------------------+
| 18 | SYP | 4675 | T | AS | SOC | Linda | McGaff | 40 | 2016-11-09 06:30:35 |
+----+--------+--------+-------+----------+----------+-------+--------+-------------+----------------------+
| 20 | 11 | GEO | 4340 | AS | SGS | Teddy | Graham | 11 | 2017-03-20 10:26:31 |
+----+--------+--------+-------+----------+----------+-------+--------+-------------+----------------------+
Thanks for any help and insight!
I think you want this. Just join your two tables together, and then do an additional join to a subquery on the status table to find the latest record for each parent proposal.
SELECT
p.Proposal_ID,
SUBSTR(p.Proposal_Name, 1, 3) AS Prefix,
SUBSTR(p.Proposal_Name, 4, 6) AS Number,
p.Institution,
p.CollegeCode,
p.DepartmentCode,
p.Proposer_FirstName,
p.Proposer_LastName,
s1.Status_Code,
s1.Status_Time
FROM proposals.proposal p
LEFT JOIN proposals.status s1
ON p.ID = s1.Parent_Proposal
INNER JOIN
(
SELECT Parent_Proposal, MAX(Status_Time) AS Max_Status_Time
FROM proposals.status
GROUP BY Parent_Proposal
) s2
ON s1.Parent_Proposal = s2.Parent_Proposal AND s1.Status_Time = s2.Max_Status_Time
WHERE
p.Institution = 'T';
I'm trying to get a distinct list of results, distinct based on user, where the selected result would be based on a set of parameters. To break it down, I have users, logs, and files. Each user can be on multiple logs and can have multiple files. Files CAN be associated with logs or not, and can also have a 'billing' flag set to true. What I'm trying to do when someone selects a log is bring up the list of files most closely associated with both the 'billing' flag and the log.
If the user has a file that is associated with the log AND has the
'billing' flag set to true, that is the result for that user.
If that is not available, the next would be the file that only has the 'billing' flag set to true (associated with any highest log or none).
If that is not available, the highest log number.
Here is the generalization of the tables:
Test Table:
+----+------+-----+
| ID | user | log |
+----+------+-----+
| 1 | 1 | 2 |
| 2 | 1 | 2 |
| 3 | 2 | 2 |
| 4 | 3 | 2 |
| 5 | 3 | 2 |
| 6 | 4 | 2 |
+----+------+-----+
File Table:
+----+-------+-----+---------+------+
| ID | file | log | billing | user |
+----+-------+-----+---------+------+
| 1 | a.pdf | 2 | 0 | 1 |
| 2 | b.pdf | 3 | 1 | 1 |
| 3 | c.pdf | 1 | 0 | 2 |
| 4 | d.pdf | 2 | 1 | 2 |
| 5 | e.pdf | 1 | 0 | 3 |
| 6 | f.pdf | 3 | 0 | 3 |
| 7 | g.pdf | 0 | 1 | 4 |
| 8 | h.pdf | 1 | 0 | 4 |
| 9 | i.pdf | 2 | 1 | 4 |
| 10 | j.pdf | 3 | 0 | 4 |
+----+-------+-----+---------+------+
In this case I would want to get:
+------+-------+-----+---------+
| user | file | log | billing |
+------+-------+-----+---------+
| 1 | b.pdf | 3 | 1 |
| 2 | d.pdf | 2 | 1 |
| 3 | f.pdf | 3 | 0 |
| 4 | i.pdf | 2 | 1 |
+------+-------+-----+---------+
My simplified query so far returns all files for the users but I'm having trouble grouping based on the above parameters.
SELECT
user,
file,
log,
billing
FROM
files
WHERE
user IN (
SELECT
DISTINCT(user)
FROM
tests
WHERE
log = 2
)
ORDER BY
CASE
WHEN log = 2 AND billing = 1 THEN 1
WHEN billing = 1 THEN 2
ELSE -1
END
Any help would be greatly appreciated.
You can use a separate query to get the results based on each of the 3 criteria specified in the OP, then UNION the results from these queries and fetch result from first query if available, otherwise from second query, otherwise from third query:
SELECT user, file, log, billing
FROM (
SELECT #row_number:=CASE WHEN #user=user THEN #row_number+1
ELSE 1
END AS row_number,
#user:=user AS user,
file, log, billing
FROM (
-- 1st query: has biggest priority
SELECT 1 AS pri, t.user, f.file, f.log, f.billing
FROM (SELECT DISTINCT user, log
FROM tests
WHERE log = 2) AS t
INNER JOIN files AS f
ON (t.user = f.user AND t.log = f.log AND f.billing = 1)
UNION ALL
-- 2nd query: priority = 2
SELECT 2 AS pri, t.user, f.file, f.log, f.billing
FROM (SELECT DISTINCT user, log
FROM tests
WHERE log = 2) AS t
INNER JOIN files AS f
ON (t.user = f.user AND f.billing = 1)
WHERE f.log > t.log OR f.log = 0
UNION ALL
-- 3rd query: priority = 3
SELECT 3 AS pri, t.user, f.file, f.log, f.billing
FROM (SELECT DISTINCT user, log
FROM tests
WHERE log = 2) AS t
INNER JOIN files AS f ON (t.user = f.user)
ORDER BY user, pri, log DESC ) s ) r
WHERE r.row_number = 1
ORDER BY user
pri column is used so as to discern and prioritize results between the three separate queries. #row_number and #user variables are used in order to simulate ROW_NUMBER() OVER (PARTITION BY user ORDER BY pri) window function. Using #row_number in the outermost query we can select the required record, i.e. the record having the highest priority within each 'user' partition.
SQL Fiddle Demo
I have one problem that I can't resolve.
I have 2 tables.
Table 1:
ID | Time
1 | 08:12:54
2 | 08:15:40
3 | 09:30:01
4 | 10:15:15
5 | 10:56:12
6 | 11:00:03
Table 2:
ID | Name| Previous | Current
1 | Queue | null | 11
2 | Queue | 11 | 19
3 | Queue | 19 | 11
3 | List | null | 11
4 | Queue | 11 | 16
4 | List | null | 11
5 | Queue | null | 15
6 | Queue | 15 | 19
The result wanted:
NumberQueue | Start | End
11 | 08:12:54 | 08:15:40
19 | 08:15:40 | 09:30:01
11 | 09:30:01 | 10:15:15
15 | 10:56:12 | 11:00:03
...
...
The previous and the current fields, have the number of the Queue and I want to know for each Queue, the start date and the end date, knowing that the previous has the previous Queue, and the current has the new Queue.
I want one query that can present this result. Help me. :(
Regards.
SELECT t1outer.ID, t1outer.Time AS start, (
SELECT Time FROM Table1 AS t1inner
WHERE t1inner.ID > t1outer.ID
ORDER BY ID ASC LIMIT 1
) AS end, Table2.Previous, Table2.Current
FROM Table1 AS t1outer
LEFT JOIN Table2 USING (ID);
This select statement should provide the information you need:
SELECT Current AS Number, t1out.Time AS Start, (
SELECT Time FROM Table1 AS t1in
WHERE t1in.ID > t1out.ID
ORDER BY ID ASC LIMIT 1
) AS End FROM Table2
LEFT JOIN Table1 AS t1out USING (ID)
WHERE Table2.Name = 'Queue';