OK, here is my SQL, and I'm using MySQL:
SELECT
assets.id,
IF(max(asset_checkins.time) IS NULL AND max(asset_checkouts.time) IS NOT NULL, 'checked-out',
IF(max(asset_checkouts.time) > max(asset_checkins.time), 'checked-out',
'checked-in')
) 'status',
asset_checkouts.user, asset_checkouts.location
FROM
assets
left outer join asset_checkouts on asset_checkouts.asset = assets.id
left outer join asset_checkins on asset_checkins.asset = assets.id
group by assets.id;
The problem is that the user and location columns are not coming from the row corresponding to the max(psdl_asset_checkouts.time) for that asset.
Instead I get:
id status user location
15 checked-out 1 4<-this
16 checked-out 1 4
When I want to get:
id status user location
15 checked-out 1 7<-this
16 checked-out 1 4
Here is the asset_checkouts table, the value I want is the "7"; the value corresponding to the max(time) for asset 15.
id user asset time location
3 1 15 7/30/12 12:29 4
14 1 15 7/31/12 11:01 7
My thought is that I will need to do a sub-select, but I'm not sure the best way of doing it.
It's not pretty but it works. It will return the latest status of the asset and the user and location where it was either checked out or checked in. This might have worked a little better if the checkins and checkouts were in the same table.
SELECT id,`status`,user,location FROM
(
SELECT a.id, 'checked-out' `status`,co.`time`, co.user, co.location
FROM assets a left join asset_checkouts co on a.id=co.asset
UNION ALL
SELECT a.id, 'checked-in' `status`, ci.`time`, ci.user, ci.location
FROM assets a left join asset_checkins ci on a.id=ci.asset
WHERE NOT ci.`time` IS NULL
) U
WHERE `time` = GREATEST((SELECT COALESCE(MAX(`time`),0)
FROM asset_checkouts co
WHERE co.asset=U.id),
(SELECT COALESCE(MAX(`time`),0)
FROM asset_checkins ci
WHERE ci.asset=U.id))
Related
I have two tables in the datbase to store client basic info (name, location, phone number) and another table to store client related transactions (date_sub, profile_sub,isPaid,date_exp,client_id) and i have an html table to view the client basic info and transaction if are available, my problem that i can't get a query to select the client info from table internetClient and from internetclientDetails at the same time, because query is only resulting when client have trans in the detail table. the two table fields are as follow:
internetClient
--------------------------------------------------------
id full_name location phone_number
-------------------------------------------------------
4 Joe Amine beirut 03776132
5 Mariam zoue beirut 03556133
and
internetclientdetails
--------------------------------------------------------------------------
incdid icid date_sub date_exp isPaid sub_price
----------------------------------------------------------------------------
6 4 2018-01-01 2018-01-30 0 2000
7 5 2017-01-01 2017-01-30 0 1000
8 4 2018-03-01 2018-03-30 1 50000
9 5 2018-05-01 2019-05-30 1 90000
// incdid > internetClientDetailsId
// icid> internetClientId
if client have trans in orderdetails, the query should return value like that:
client_id full_name date_sub date_exp isPaid sub_price
-------------------------------------------------------------------------------------
4 Joe Amine 2018-03-01 2018-03-30 1 50000
5 Mariam zoue 2018-05-01 2019-05-30 1 90000
else if the client has no id in internetOrederDetails
--------------------------------------------------------
icid full_name location phone_number
-------------------------------------------------------
4 Joe Amine beirut 03776132
5 Mariam zoue beirut 0355613
Thanks in advance
try with left join. It will display all records from internetClient and related record from internetclientdetails
Select internetClient.id, internetClient.full_name
, internetClient.location, internetClient.phone_number
, internetclientdetails.incdid, internetclientdetails.icid
, internetclientdetails.date_sub, internetclientdetails.date_exp
, internetclientdetails.isPaid, internetclientdetails.sub_price
from internetClient
left join internetclientdetails
on internetClient.id=internetclientdetails.icid group by internetclientdetails.icid order by internetclientdetails.incdid desc
if you want to get records of, only paid clients then you can try the following
Select internetClient.id, internetClient.full_name
, internetClient.location, internetClient.phone_number
, internetclientdetails.icid, internetclientdetails.incdid
, internetclientdetails.date_sub, internetclientdetails.date_exp
, internetclientdetails.isPaid, internetclientdetails.sub_price
from internetClient
left join internetclientdetails
on internetClient.id=internetclientdetails.icid
and internetclientdetails.isPaid=1 group by internetclientdetails.icid
order by internetclientdetails.incdid desc
SUMMARY
We generate a dataset containing just the ICID and max(date_sub) (alias:ICDi) We join this to the InternetClientDetails (ICD) to obtain just the max date record per client. Then left join this to the IC record; ensuring we keep all InternetClient(IC) records; and only show the related max Detail Record.
The below approach should work in most mySQL versions. It does not use an analytic which we could use to get the max date instead of the derived table provided the MySQL version you use supported it.
FINAL ANSWER:
SELECT IC.id
, IC.full_name
, IC.location
, IC.phone_number
, ICD.icid
, ICD.incdid
, ICD.date_sub
, ICD.date_exp
, ICD.isPaid
, ICD.sub_price
FROM internetClient IC
LEFT JOIN (SELECT ICDi.*
FROM internetclientdetails ICDi
INNER JOIN (SELECT max(date_sub) MaxDateSub, ICID
FROM internetclientdetails
GROUP BY ICID) mICD
ON ICDi.ICID = mICD.ICID
AND ICDi.Date_Sub = mICD.MaxDateSub
) ICD
on IC.id=ICD.icid
ORDER BY ICD.incdid desc
BREAKDOWN / EXPLANATION
The below gives us a subset of max(date_Sub) for each ICID in clientDetails. We need to so we can filter out all the records which are not the max date per clientID.
(SELECT max(date_sub) MaxDateSub, ICID
FROM internetclientdetails
GROUP BY ICID) mICD
Using that set we join to the details on the Client_ID's and the max date to eliminate all but the most recent detail for each client. We do this because we need the other detail attributes. This could be done using a join or exists. I prefer the join approach as it seems more explicit to me.
(SELECT ICDi.*
FROM internetclientdetails ICDi
INNER JOIN (SELECT max(date_sub) MaxDateSub, ICID
FROM internetclientdetails
GROUP BY ICID) mICD
ON ICDi.ICID = mICD.ICID
AND ICDi.Date_Sub = mICD.MaxDateSub
) ICD
Finally the full query joins the client to the detail keeping client even if there is no detail using a left join.
COMPONENTS:
You wanted all records from InternetClient (FROM internetClient IC)
You wanted related records from InternetClientDetail (LEFT Join InternetClientDetail ICD) while retaining teh records from InternetClient.
You ONLY wanted the most current record from InternetClientDetail (INNER JOIN InternetClientDetail mICD as a derived table getting ICID and max(date))
Total record count should = total record count in InternetClient which means all relationships must be a 1:1o on the table joins -- one-to-one Optional.
I have two tables with multiple rows. The first is called comments and has the following structure:
user_id last_commented
........................
9239289 2017-11-06
4239245 2017-11-05
4239245 2017-11-03
6239223 2017-11-02
1123139 2017-11-04
The second one is called users and has the following structure:
user_id user_name user_status
.................................
9239289 First Name 0
4239245 First Name2 2
6239223 First Name3 1
1123139 First Name4 2
I need a query that displays the users who have not added comments for the last 3 days, have a user_status equals to 2 and display the number of days since they last commented.
This is my query so far:
select u.*
from users u
where not exists (
select 1
from comments c
where c.user_id = u.user_id and last_commented > DATE(NOW()) - INTERVAL 3 DAY
) and user_status = 2
Which outputs correctly the users who haven't commented for the last 3 days. How can I modify it so it shows the number of days since they last commented?
If you need the number of days since the last comment, then you will need a join, of some sort:
select u.*, datediff(curdate(), last_commented)
from users u left join
comments c
on c.user_id = u.user_id
where u.status = 2
group by u.user_id
having max(last_commented) < curdate() - interval 3 day or
max(last_commented) is null;
This version includes users who have not commented at all.
I have the following three tables to look after support tickets in a small web application, but I need some help getting the data I need.
Table 1 (ticket):
user_ID site_ID support_ID timestamp priority title
12 25 3 2014-09-26 14:09:25 0 A Test Row
12 26 4 2014-09-27 09:41:18 0 A 2nd Test Row
Table 2 (ticket_reply):
reply_ID support_ID user_ID support_reply reply_timestamp
3 3 12 some really boring text 2014-09-26 14:09:25
4 3 25 some really boring reply 2014-09-26 15:35:18
5 4 12 some really boring text 2014-09-27 09:41:18
Table 3 (ticket_status):
ticket_status_ID support_ID status_ID status_timestamp
3 3 40 2014-09-26 14:09:25
4 3 41 2014-09-26 15:35:18
5 4 40 2014-09-27 09:41:18
The 1st table holds the key ticket information, the 2nd, any replies made to the corresponding ticket, and the third tracks the change in status (statuses are held in another table, but don't need anything from there).
What I need to do is get the number of tickets where the latest status is == 40, and if this is greater than 0, get the latest reply along with the data from the first table.
I've tried multiple ways of doing this, but I am stuck. Don't really want to paste them here as they will likely confuse people, and I doubt they are even close.
This one was rather tricky, however here is a working solution for you.
This query will get the most recent support_reply value for all tickets where the most recent status_ID is 40.
SELECT
ticket_status_ID,
support_ID,
status_ID,
status_timestamp,
reply_ID,
support_reply,
reply_timestamp,
`timestamp` ticket_timestamp,
`priority` ticket_priority,
title
FROM (
SELECT * FROM (
SELECT * FROM (
SELECT
ticket_status.ticket_status_ID,
ticket_status.support_ID,
ticket_status.status_ID,
ticket_status.status_timestamp,
ts1.reply_ID,
ts1.user_ID,
ts1.support_reply,
ts1.reply_timestamp
FROM
ticket_status
INNER JOIN (SELECT * FROM ticket_reply ORDER BY reply_timestamp DESC) ts1 ON ts1.support_ID = ticket_status.support_ID
GROUP BY support_ID, status_ID
ORDER BY status_timestamp DESC
) ts2
GROUP BY ts2.support_ID
) ts3
INNER JOIN (SELECT support_ID as `ticket_support_ID`, site_ID, `timestamp`, priority, title FROM ticket) ts4 ON ts4.ticket_support_ID = ts3.support_ID
WHERE ts3.status_ID = 40
) ts5
From the example given, it looks that all timestamp are equivalent, so a query like this should be enough:
SELECT
ticket.*,
ticket_reply.*
FROM
(SELECT support_ID, MAX(status_timestamp) as max_timestamp
FROM ticket_status
GROUP BY support_ID) m
INNER JOIN ticket
ON m.support_ID=ticket.support_ID
AND m.max_timestamp=ticket.`timestamp`
INNER JOIN ticket_reply
ON m.support_ID=ticket_reply.support_ID
AND m.max_timestamp=ticket_reply.reply_timestamp
INNER JOIN ticket_status
ON m.support_ID=ticket_status.support_ID
AND m.max_timestamp=ticket_status.status_timestamp
WHERE
status_ID=40;
but depending on the logic of your application, it might happen that the last row in a table has a timestamp of 2014-09-27 09:41:18 and the last in another has for example 2014-09-27 09:41:19.
In this case, you should use a query like this one:
SELECT
ticket.*,
ticket_reply.*
FROM
(SELECT support_ID, MAX(status_timestamp) AS max_status_timestamp
FROM ticket_status
GROUP BY support_ID) m_status
INNER JOIN
(SELECT support_ID, MAX(reply_timestamp) AS max_reply_timestamp
FROM ticket_reply
GROUP BY support_ID) m_reply
ON m_status.support_ID=m_reply.support_ID
INNER JOIN
(SELECT support_ID, MAX(`timestamp`) AS max_ticket_timestamp
FROM ticket
GROUP BY support_ID) m_ticket
ON m_status.support_ID=m_ticket.support_ID
INNER JOIN ticket_status
ON ticket_status.support_ID=m_status.support_ID
AND ticket_status.status_timestamp=m_status.max_status_timestamp
INNER JOIN ticket_reply
ON ticket_reply.support_ID=m_reply.support_ID
AND ticket_reply.reply_timestamp=m_reply.max_reply_timestamp
INNER JOIN ticket
ON ticket.support_ID=m_ticket.support_ID
AND ticket.`timestamp`=m_ticket.max_ticket_timestamp
WHERE
ticket_status.status_ID=40;
Please see fiddle here.
You can try this one:
SELECT t.*, tr.support_reply, ts.status_timestamp
FROM ticket_status as ts
left join ticket_reply as tr on(ts.support_ID=tr.support_ID)
left join ticket as t on(t.support_ID=tr.support_ID)
where status_ID=40
order by status_timestamp desc
limit 1;
I have a table for terminal
Id status
1 Online
2 Offline
3 Offline
and I have a separate table where I can find the total hours/date of the up and downtime.
total_time
Id up down
1 10 14
2 20 4
3 15 9
1 5 19
2 4 20
3 10 14
I want to display the terminal id, status and the TOTAL up and downtime(1 = 15(up), 2 = 24(up), 3 = 25(up). I'm using inner join and I have no idea how i will be able to get the sum of the up and downtime..
SELECT terminal.Id, terminal.status, total_time.Id, SUM(total_time.up),SUM(total_time.down)
FROM terminal
INNER JOIN total_time
ON terminal.Id = total_time.Id
WHERE terminal.Id = total_time.Id
Something like this should do the trick. I am interpreting your question as asking for only the sum of the time of the current status. If this is not what you want (and maybe you want the sum of both times), please let me know.
SELECT t.id, t.status, IF(t.status = 'Online', ttlTime.upTime, ttlTime.downTime) as totalTime
FROM terminal t
JOIN
(SELECT tt.id, SUM(tt.up) as upTime, SUM(tt.down) AS downTime
FROM total_time tt
GROUP BY tt.id) ttlTime ON t.id = ttlTime.id
See the SQLFiddle
You get "inner sums" by using the GROUP BY clause.
Try:
SELECT terminal.Id, SUM(total_time.up), SUM(total_time.down)
FROM terminal
INNER JOIN total_time
ON terminal.Id = total_time.Id
GROUP BY terminal.Id
SELECT DISTINCT terminal.Id, terminal.status, total_time.Id, SUM(total_time.up),SUM(total_time.down)
FROM terminal
INNER JOIN total_time
ON terminal.Id = total_time.Id
Should do it. The DISTINCT clause acts as a filter to remove duplicate records from a result set.
I have a two tables first one is called teams and second one is called cpd and I want this result required (see result screen below). I tried myself but was not successful (see practice query below).
teams table
id name sub_cat_id
1 SACRAMENTO KINGS 19
2 KINGS 19
3 MIMAMI HEAT 19
4 HEAT 20
5 KITE 20
cpd table
id team_id status added_date
1 3 1 2012-05-26
2 3 1 2012-05-27
3 3 0 2012-05-28
practice Query
SELECT
t.`id`,t.`name`,IFNULL(cpd.status,0) AS resultStatus,IFNULL(cpd.added_date,CURDATE()) AS added_date
FROM `teams` t
LEFT JOIN cpd ON cpd.team_id = t.id
WHERE t.`sub_cat_id` = 19 OR cpd.added_date = CURDATE()
Result Screen (Required only those rows are black color in screen)
Update
Explanation ?
I am trying to get those rows who they are related with sub_cat_id = 19 like this in team table
Join team table with cpd table for cpd.status filed
cpd.status must be related with current date in cpd table like 2012-05-28
There are more than one way to get the desired result:
For example:
SELECT t.`id`,t.`name`,
IFNULL(cpd.status,0) AS resultStatus,
IFNULL(cpd.added_date,CURDATE()) AS added_date
FROM `teams` t
INNER JOIN cpd ON (cpd.team_id = t.id AND cpd.status = 0)
WHERE t.`sub_cat_id` = 19
OR
cpd.added_date = CURDATE()
Your JOIN ON cpd.team_id = t.idonly matches one tuple with the cpd table so for the other tuples date is set as NULL (because you are doing LEFT JOIN) and hence the where query gives only one tuple
SELECT
t.id,t.name,IFNULL(cpd.status,0) AS resultStatus,IFNULL(cpd.added_date,CURDATE()) AS added_date
FROM teams t
LEFT JOIN cpd ON cpd.team_id = t.id
WHERE t.sub_cat_id = 19 OR cpd.added_date = CURDATE()
GROUP BY t.id