SQL : Geeting Order's status timestamp in a nested query - mysql

I'm working on a Order Table which has all the details regarding the order's that were allocated. The sample DB example is
Order ID Order Status Action Date
23424 ALC 1571467792094280
23424 PIK 1571467792999990
23424 PAK 1571469792999990
23424 SHP 1579967792999990
33755 ALC 1581467792238640
33755 PIK 1581467792238640
33755 PAK 1581467792238640
33755 SHP 1581467792238640
In the table I have order ID , status, action_date (the action dates updated when ever there is an update on order status against the update timestamp, the action_date is unix time)
I'm trying to write a query that can provide me the Order ID, ALC_AT, PIK_AT, PAK_AT, SHP_AT
Basically all the timestamp updates against a Order ID within one row, I know it can be done via Nested Query but, I'm unable to figure how how to do it.
Any help would be highly appreciated.
Edit (As asked to provide the sample result ) :
Order ID Order Status ALC_AT PIK_AT PAK_AT SHP_AT
23424 SHP 1571467792094280 1571467792999990 1571469792999990 1579967792999990

I am not sure how it is done in mysql. But below describes how it will be done in Oracle.
You can searh more for PIVOT in mysql to help you in the same.
select *
from (select order_id,
status,
action_date
from order)
pivot (max(status)
for app_id in ( 'ALC' as 'ALC_AT', 'PIK' as 'PIK_AT', 'PAK' as 'PAK_AT', 'SHP' as 'SHP_AT'))
Hope this will help you.
EDIT for mysql:
select *
from (select "order.order_number",
"shipment.status",
from_unixtime("action_date"/1000000) as "action_date"
from order_table
where "order.order_number" = '2019-10-19-N2-6411')
pivot (max("action_date")
for "shipment_status" in ( 'ALC' AS 'ALC_AT', 'PIK' AS 'PIK_AT', 'PAK'
AS 'PAK_AT', 'SHP' AS 'SHP_AT'))

Related

GROUP BY id showing newest entry of same ID

I know this has already been asked answered a thousend times. But I seem not to be able to resolve this.
I am trying to group by and order this query so I can join it as subquery to a bigger query
SELECT * FROM `ggorderlog`
WHERE `GGTITLE` LIKE '%Reklamation%'
ORDER BY `GGDATE` DESC, `ggorderlog`.`GGORDERID` DESC
This is the result from the ggorderlog table and the query above
GGTITLE GGOXID GGDATE GGORDERID GGTITLE User
Reklamation uniqueid1 2018.12.7 16:20:00 1 Reklamation created Max Mustermann
Reklamation uniqueid2 2018.12.7 16:24:00 1 Reklamation finished Maxine Musterfrau
Reklamation uniqueid3 2018.12.7 16:22:00 2 Reklamation created Max mustermann
Now what I want is to have this table be displayed so that for every GGORDERID I only see the latest entry. In order to give an overview over the User who has worked on this and the status of the ticket.
Like this:
GGTITLE GGOXID GGDATE GGORDERID GGTITLE User
Reklamation uniqueid2 2018.12.7 16:24:00 1 Reklamation finished Maxine Musterfrau
Reklamation uniqueid3 2018.12.7 16:22:00 2 Reklamation created Max mustermann
I tried standard group by with order by but mysql seem to do the group by first and give out a random column
I tried this but it still shows always a random date.
Select* from (
Select *
from ggorderlog as b
where GGTITLE like '%Reklamation%'
ORDER BY b.GGDATE DESC
) b2
group by b2.GGORDERID
I tried a lot of other suggestions with left itselfe or group_concat and then desolve again but nothing seems to work.
Can you try this ?
SELECT gg.*
FROM ggorderlog gg
INNER JOIN
(SELECT ggorderid, MAX(ggdate) AS maxggdate,oxid
FROM ggorderlog
GROUP BY ggorderid) groupedgg
ON gg.oxid = groupedgg.oxid
AND gg.ggdate = groupedgg.maxggdate

Having an issue with displaying most recent Call_date

I am trying to output the most recent Call_date. I have tried using the MAX function with no luck. Below I have tagged 3 images showing the database tables, my current code output and the required output. Underneath that is my current code. Any help is appreciated!
Database Tables - https://imgur.com/a/7ZPFO
Output we are looking for - https://imgur.com/a/k3idB
Output my code currently gives - https://imgur.com/a/H53vq
Here is what I have tried:
SELECT Staff.First_name, Staff.Last_name, call_date, taken_by
FROM Issue
JOIN Caller ON Issue.Caller_id = Caller.Caller_id
JOIN Staff ON Issue.Taken_by = Staff.Staff_code
WHERE Caller.First_name = 'Harry'
I would just add the following to the end of your query:
ORDER BY call_date DESC LIMIT 1
This will give you one row as a result. And that row will be the one with the most recent call_date.
Based on the code provided, you're only asking for 3 columns so it's a matter of a join. When you select the max call date you need to group by the other 2 non-aggregate columns. If the date column is of datatype Date or Datetime then this should work:
SELECT Caller.First_name, Caller.Last_name --from Caller_id
,MAX(Issue.call_date) AS call_date
FROM Issue INNER JOIN Caller ON Issue.Caller_id = Caller.Caller_id
WHERE Caller.First_name = 'Harry'
GROUP BY Caller.First_name, Caller.Last_name

MySQL Ignoring Outliers

I have to present some data to work colleagues and i am having issues analysing it in MySQL.
I have 1 table called 'payments'. Each payment has columns for:
Client (our client e.g. a bank)
Amount_gbp (the GBP equivalent of the value of the transaction)
Currency
Origin_country
Client_type (individual or company)
I have written pretty simple queries like:
SELECT
AVG(amount_GBP),
COUNT(client) AS '#Of Results'
FROM payments
WHERE client_type = 'individual'
AND amount_gbp IS NOT NULL
AND currency = 'TRY'
AND country_origin = 'GB'
AND date_time BETWEEN '2017/1/1' AND '2017/9/1'
But what i really need to do is eliminate outliers from the average AND/OR only include results within a number of Standard Deviations from the Mean.
For example, ignore the top/bottom 10 results of 2% of results etc.
AND/OR ignore any results that fall outside of 2 STDEVs from the Mean
Can anyone help?
--- EDITED ANSWER -- TRY AND LET ME KNOW ---
Your best best is to create a TEMPORARY table with the avg and std_dev values and compare against them. Let me know if that is not feasible:
CREATE TEMPORARY TABLE payment_stats AS
SELECT
AVG(p.amount_gbp) as avg_gbp,
STDDEV(amount_gbp) as std_gbp,
(SELECT MIN(srt.amount_gbp) as max_gbp
FROM (SELECT amount_gbp
FROM payments
<... repeat where no p. ...>
ORDER BY amount_gbp DESC
LIMIT <top_numbers to ignore>
) srt
) max_g,
(SELECT MAX(srt.amount_gbp) as min_gbp
FROM (SELECT amount_gbp
FROM payments
<... repeat where no p. ...>
ORDER BY amount_gbp ASC
LIMIT <top_numbers to ignore>
) srt
) min_g
FROM payments
WHERE client_type = 'individual'
AND amount_gbp IS NOT NULL
AND currency = 'TRY'
AND country_origin = 'GB'
AND date_time BETWEEN '2017/1/1' AND '2017/9/1';
You can then compare against the temp table
SELECT
AVG(p.amount_gbp) as avg_gbp,
COUNT(p.client) AS '#Of Results'
FROM payments p
WHERE
p.amount_gbp >= (SELECT (avg_gbp - std_gbp*2)
FROM payment_stats)
AND p.amount_gbp <= (SELECT (avg_gbp + std_gbp*2)
FROM payment_stats)
AND p.amount_gbp > (SELECT min_g FROM payment_stats)
AND p.amount_gbp < (SELECT max_g FROM payment_stats)
AND p.client_type = 'individual'
AND p.amount_gbp IS NOT NULL
AND p.currency = 'TRY'
AND p.country_origin = 'GB'
AND p.date_time BETWEEN '2017/1/1' AND '2017/9/1';
-- Later on
DROP TEMPORARY TABLE payment_stats;
Notice I had to repeat the WHERE condition. Also change *2 to whatever <factor> to what you need!
Still Phew!
Each compare will check a different stat
Let me know if this is better

SQL select from date ranges

I have a table of RADIUS session records that includes start time, stop time, and MAC address. I have a requirement to collect a list of users that were online during two time ranges. I believe I'm getting a list of all users online during the time ranges with the following query:
SELECT s_session_id, s_start_time, s_stop_time, s_calling_station_id
FROM sessions
WHERE (
("2015-10-01 08:00:00" BETWEEN s_start_time AND s_stop_time OR "2015-10-01 08:30:00" BETWEEN s_start_time AND s_stop_time)
OR
("2015-10-01 12:00:00" BETWEEN s_start_time AND s_stop_time OR "2015-10-01 12:30:00" BETWEEN s_start_time AND s_stop_time)
)
ORDER BY s_start_time;
But the next step, isolating details for only those users online during both periods, is eluding me. The closest I get is adding
GROUP BY s_calling_station_id HAVING COUNT(s_calling_station_id) > 1
but that doesn't provide me with all the session details.
Fiddle is here: http://sqlfiddle.com/#!9/1df471/1
Thanks for any assistance!
Use a self-join. Use column aliases so you can access the columns from each session with different names.
SELECT s1.s_calling_station_id,
s1.s_session_id AS s1_session_id, s1.s_start_time AS s1_start_time, s1.s_stop_time AS s1_stop_time,
s2.s_session_id AS s2_session_id, s2.s_start_time AS s2_start_time, s2.s_stop_time AS s2_stop_time
FROM sessions AS s1
JOIN sessions AS s2
ON s1.s_calling_station_id = s2.s_calling_station_id
AND s1.s_session_id != s2.s_session_id
WHERE ("2015-10-01 08:00:00" BETWEEN s1.s_start_time AND s1.s_stop_time OR "2015-10-01 08:30:00" BETWEEN s1.s_start_time AND s1.s_stop_time)
AND
("2015-10-01 12:00:00" BETWEEN s2.s_start_time AND s2.s_stop_time OR "2015-10-01 12:30:00" BETWEEN s2.s_start_time AND s2.s_stop_time)
DEMO
Although this question already has an accepted answer, I'd like to add this one (it avoids duplicates and pulls the data from the sessions table of all sessions that fulfill the condition):
First, create a table that holds the filtered data (the MAC addresses that have connections on both intervals:
create table temp_sessions
select s1.s_calling_station_id
, if(#t1_1 between s1.s_start_time and s1.s_stop_time or #t1_2 between s1.s_start_time and s1.s_stop_time, s1.s_session_id, null) as s_1
, if(#t2_1 between s2.s_start_time and s2.s_stop_time or #t2_2 between s2.s_start_time and s2.s_stop_time, s2.s_session_id, null) as s_2
from -- I use user variables because it will make easier to modify the time intervals if needed
(select #t1_1 := '2015-10-01 08:00:00', #t1_2 := '2015-10-01 08:30:00'
, #t2_1 := '2015-10-01 12:00:00', #t2_2 := '2015-10-01 12:30:00') as init
, sessions as s1
inner join sessions as s2
on s1.s_calling_station_id = s2.s_calling_station_id
and s1.s_session_id != s2.s_session_id
having s_1 is not null and s_2 is not null;
And now, simply use this table to get what you need:
select sessions.*
from sessions
inner join (
select s_calling_station_id, s_1 as s_session_id
from temp_sessions
union
select s_calling_station_id, s_2 as s_session_id
from temp_sessions
) as a using (s_calling_station_id, s_session_id);
Here's the SQL fiddle

GROUP BY HAVING not working as expected

I'm struggling with what should be a simple query.
An event table stores user activity in an application. Each click generates a new event and datetime stamp. I need to show a list of recently accessed records having the most recent datetime stamp. I need to only show the past 7 days of activity.
The table has an auto-increment field (eventID), which corresponds with the date_event field, so it's better to use that for determining the most recent record in the group.
I found that some records are not appearing in my results with the expected most recent datetime. So I stripped my query down the basics:
NOTE that the real-life query does not look at custID. I am including it here to narrow down on the problem.
SELECT
el.eventID,
el.custID,
el.date_event
FROM
event_log el
WHERE
el.custID = 12345 AND
el.userID=987
GROUP BY
el.custID
HAVING
MAX( el.eventID )
This is returned:
eventID custID date_event
346290 12345 2013-06-21 09:58:44
Here's the EXPLAIN
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE el ref userID,custID,Composite custID 5 const 203 Using where
If I change the query to use HAVING MIN, the results don't change.. I should see a different eventID and date_event, as there are dozens of records matching the custID and userID.
SELECT
el.eventID,
el.custID,
el.date_event
FROM
event_log el
WHERE
el.custID = 12345 AND
el.userID=987
GROUP BY
el.custID
HAVING
MIN( el.eventID )
Same results as before:
eventID custID date_event
346290 12345 2013-06-21 09:58:44
No change.
This tells me I have another problem, but I am not seeing what that might be.
Some pointers would be appreciated.
SELECT
el.eventID,
el.custID,
el.date_event
FROM
event_log el
WHERE
el.custID = 12345 AND
el.userID=987 AND
el.eventID IN (SELECT MAX(eventID)
FROM event_log
WHERE custID = 12345
AND userID = 987)
Your query doesn't work because you misunderstand what HAVING does. It evaluates the expression on each line of the result set, and keeps the rows where the expression evaluates to true. The expression MAX(el.eventID) simply returns the maximum event ID selected by the query, it doesn't compare the current row to that event ID.
Another way is:
SELECT
el.eventID,
el.custID,
el.date_event
FROM
event_log el
WHERE
el.custID = 12345 AND
el.userID=987
ORDER BY eventID DESC
LIMIT 1
The more general form that works for multiple custID is:
SELECT el.*
FROM event_log el
JOIN (SELECT custID, max(date_event) maxdate
FROM event_log
WHERE userID = 987
GROUP BY custID) emax
ON el.custID = emax.custID AND el.date_event = emax.maxdate
WHERE el.userID = 987
You can use a group function in a statement containing no GROUP BY clause, but it would be equivalent to grouping on all rows. But I guess you're looking for the common syntax,
SELECT
MIN(el.eventID) AS `min_eventID`, --> Yes it is wrong :(
el.custID,
el.date_event
FROM
event_log el
WHERE
el.userID = 987
GROUP BY el.custID;
But disagreements are welcome .
[ Edit ]
I think I didn't show a solution fast enough... but maybe you're rather looking for the fastest solution.
Assuming field date_event defaults to CURRENT_TIMESTAMP (am I wrong?), ordering by date_event would be a waste of time (and money, thus).
I've made some tests with 20K rows and execution time was about 5ms.
SELECT STRAIGHT_JOIN y.*
FROM ((
SELECT MAX(eventId) as eventId
FROM event_log
WHERE userId = 987 AND custId = 12345
)) AS x
INNER JOIN event_log AS y
USING (eventId);
Maybe (possibly, who knows) you didn't get the straight_join thing; as documented on the scriptures, STRAIGHT_JOINs are similar to JOINs, except that the left table is always read before the right table. Sometimes it's useful.
For your specific situation, we're likely to filter to a certain eventID before (on table "x"), not to retrieve 99,99% useless rows from table "y".
More disagreements expected in 3, 2, ...