If i had a table that stored a person's name, the time it took them to run a mile, and when they ran said mile, what would be the best way to get a person most recent lap-time.
LAPS
____________________________________________
| name | lap_time | date |
--------------------------------------------
| George | 20.3 | 2013-01-17 09:17:14 |
| Alex | 32.2 | 2013-02-17 14:24:32 |
| Mike | 16.6 | 2013-01-17 07:57:54 |
| Alex | 28.5 | 2013-01-17 19:50:21 |
| Mike | 15.1 | 2013-02-17 12:37:12 |
| Mike | 14.8 | 2013-03-17 06:58:34 |
''''''''''''''''''''''''''''''''''''''''''''
I've been doing it this way, and it has worked for me so far, but I'm curious to know if there is a better way.
SELECT l.lap_time
FROM laps l
INNER JOIN(
SELECT *, MAX(date) as most_recent
FROM laps
WHERE name = 'Alex'
)AS temp ON (
l.date = temp.most_recent
AND l.name = temp.name
)
The actual table that i'm using this type of query on is huge, so i'm looking for the most time efficient way of doing it.
This will work for a single name:
SELECT name, lap_time, date
FROM laps
WHERE name = 'Alex'
ORDER BY date DESC
LIMIT 1
Result
| NAME | LAP_TIME | DATE |
-----------------------------------------------------
| Alex | 32.2 | February, 17 2013 14:24:32+0000 |
See the demo
This should work for all names:
SELECT a.name, b.lap_time, a.date
FROM
(SELECT name, MAX(date) AS date FROM laps GROUP BY name) a
LEFT JOIN laps b
ON b.date = a.date AND b.name = a.name
Result
| NAME | LAP_TIME | DATE |
-------------------------------------------------------
| Alex | 32.2 | February, 17 2013 14:24:32+0000 |
| George | 20.3 | January, 17 2013 09:17:14+0000 |
| Mike | 14.8 | March, 17 2013 06:58:34+0000 |
See the demo
I would test it with this query and with MAX to see which is faster.
Select * from laps where name = 'Alex' order by date desc limit 1;
Related
I have a query. I want to do an subtraction of the first and last row in the same day. I wrote the this query, but I was not sure of the performance. Is there an alternative way to this problem?
| imei | date | km |
|-----------------------------------------|
| 123 | 2019-01-15 00:00:01 | 15 |
| 123 | 2019-01-15 12:12:08 | 8 |
| 123 | 2019-01-15 23:00:59 | 30 |
| 456 | 2019-01-15 00:03:12 | 232 |
| 456 | 2019-01-15 07:04:00 | 123 |
| 456 | 2019-01-15 23:16:18 | 464 |
My query:
SELECT
gg.imei,
DATE_FORMAT(gg.datee, '%Y-%m-%d'),
gg.km - (SELECT
g.km
FROM
gps g
WHERE
g.datee LIKE '2019-01-15%'
AND g.datee = (SELECT
MIN(t.datee)
FROM
gps t
WHERE
t.datee LIKE '2019-01-15%'
AND t.imei = g.imei)
AND g.imei = gg.imei
GROUP BY g.imei) AS km
FROM
gps gg
WHERE
gg.datee LIKE '2019-01-15%'
AND gg.datee = (SELECT
MAX(ts.datee)
FROM
gps ts
WHERE
ts.datee LIKE '2019-01-15%'
AND gg.imei = ts.imei)
Result is true.
| imei | date | km |
|------------------------------|
| 123 | 2019-01-15 | 15 |
| 456 | 2019-01-15 | 232 |
But the query is too complicated.
Edit: There are 3 million records in the table.
You can find first and last datetime for each imei-date pair in a sub query then join with it:
SELECT agg.imei, agg.date_date, gps_last.km - gps_frst.km AS diff
FROM (
SELECT imei, DATE(date) AS date_date, MIN(date) AS date_frst, MAX(date) AS date_last
FROM gps
GROUP BY imei, DATE(date)
) AS agg
JOIN gps AS gps_frst ON agg.imei = gps_frst.imei AND agg.date_frst = gps_frst.date
JOIN gps AS gps_last ON agg.imei = gps_last.imei AND agg.date_last = gps_last.date
You need appropriate indexes on your table though. The DATE(date) part in particular will be slow, so you might want to consider adding another column for storing the date part only.
I want to sum every time and check with if condition. If condition matches I want the get the created date of the final matched row.
+------------+----------------------------------+------------+--------+
| id | EMAIL | created | Amt |
+------------+----------------------------------+------------+--------+
| 61 | abc#gmail.com | 1514909390 | 57.00 |
| 25 | xyz#gmail.com | 1515534837 | 360.00 |
| 36 | zccc#abv.com | 1515645391 | 240.00 |
| 22 | vv#aa.com | 1516419622 | 320.40 |
| 48 | aa#xyz.com | 1516706121 | 240.00 |
+------------+----------------------------------+------------+--------+
I try this query but I'm not getting the solution...
select
sum(a.amount) as amt,
if(sum(a.amount)>8000,slp.sal_time,0) as Amt_exceed_date
from employee a
join emp_user u
on a.cmp_id=u.user_id
left join emp_sal as slp
on slp.user_id=a.cmp_id
where
order by slp.sal_time;
Somewhat like row wise sum
select e.*,(
select sum(Amt)
from employee
where created <= e.created
) row_wise_sum
from employee e
having row_wise_sum < 800
order by e.created
desc limit 1
Demo
I have a staff table like this --->
+------+------------------+------+------------+--------+
| EC | Name | Code | Dob | Salary |
+------+------------------+------+------------+--------+
| 2001 | ROBBIE KEANE | VSS1 | 1990-05-16 | 18000 |
| 2002 | ANSUMAN BANERJEE | VSS1 | 1985-05-21 | 18000 |
| 2003 | OMAR GONZALEZ | SACP | 1989-04-16 | 20000 |
| 2004 | ALAN GORDON | IALO | 1989-05-03 | 20000 |
| 2005 | ROBBIE KEANE | IALO | 1988-01-16 | 18000 |
| 2006 | CHANDLER HOFFMAN | BBDP | 1988-07-17 | 22000 |
| 2007 | PAUL POGBA | BHSM | 1990-08-16 | 18000 |
| 2008 | SHINJI KAGAWA | LPDC | 1991-01-20 | 18000 |
+------+------------------+------+------------+--------+
And now i want to list those codes (like VSS1), which have less than specified number of people assigned with them(say like less than 2) , how can i do this please help.
My query up till now is-->
SELECT Code,count(*) as 'Number of Staff' from STAFF where Code IN (SELECT Code from STAFF GROUP BY CODE LIMIT 2);
But this is not working.
You can filter row count for each Code group with the HAVING clause :
SELECT Code
, COUNT(*)
FROM STAFF
GROUP BY Code
HAVING COUNT(*) < 2
If you need to know the names of the people having this count less than 2 then...
SELECT S.EC, S.Name, S.Code, S.DOB, S.Salary, SC.Code, SC.Cnt
FROM STAFF S
INNER JOIN (SELECT Count(*) cnt, Code FROM STAFF GROUP BY CODE) SC
on S.Code = SC.code
WHERE SC.CNT < 2
should work in SQL server and mySQL. Though SQL Sever could also use a windowed set which would be faster.
If however, you just need to know the Codes having less than a certain number, notulysses having clause should fit the bill.
I have a requirement to map a tax rate to a person based on the country the person was resident at that time.
tbl: person
| p_id | name_first | name_last |
=======++========================
| 1 | john | smith |
| 2 | joanne | smyth |
tbl: person_in_country
| p_id | iso | arrived |
===========================
| 1 | GB | 1980-01-01 |
| 2 | FR | 1987-03-21 |
| 1 | FR | 2003-06-17 |
| 1 | JP | 2008-07-02 |
| 2 | GB | 2008-10-01 |
| 1 | GB | 2009-01-10 |
tbl: country
| iso | ctry_name |
========================
| GB | United Kingdom |
| FR | France |
| JP | Japan |
tbl: tax_rates
| iso | tax_rate | tax_date |
===============================
| GB | 17.5 | 1970-01-01 |
| FR | 15.0 | 1977-03-21 |
| JP | 12.0 | 1977-06-17 |
| FR | 15.0 | 1994-03-21 |
| JP | 18.5 | 2008-07-02 |
| GB | 15 | 2008-04-01 |
| GB | 20 | 2010-05-01 |
So I need tuples containing the person in the country and the tax rate that they should have at a given time..
Something along the lines of:
select p.p_id, p.name_first, p.name_last,
pic.arrived,
c.iso, c.ctry_name,
t.tax_rate
from people p
left join (select * from person_in_country order by arrived desc) pic using (p_id)
left join country c on c.iso = pic.iso
left join (select * from tax_rates order by tax_date desc) t on t.iso = c.iso
where t.tax_date <= NOW()
group by p.pid, pic.arrived, t.tax_date
Hope this make sense... and many thanks in advance
Actually, you have to do a query basicly in three steps. First one you are going to retrieve a kind of "raw data" with all desired columns, joining the relating tables, whatever these columns will be used to join or to retrieve choosen data.
After that, you have to group data in order to turn only last dates from the matching join.
Finally, you have to query again tax table to retrieve tax in the tax date current at the moment of arrival.
It is possible that there is a more easy or ellegant way to do so, but this query is working. Check your system performance depending on the query demmands. It seems a bit hard at a first glance, but it isn't when taking a more carefull look. The SQL code:
SELECT
c02.iso,
c02.p_id,
c02.name_first,
c02.name_last,
c02.ctry_name,
c02.arrived,
c02.mtax_date,
tax_rates.tax_rate
FROM (
SELECT
c01.iso,
c01.p_id,
c01.name_first,
c01.name_last,
c01.ctry_name,
c01.arrived,
Max(c01.tax_date) AS mtax_date
FROM (
SELECT
country.iso,
person.p_id,
person.name_first,
person.name_last,
country.ctry_name,
person_in_country.arrived,
tax_rates.tax_date
FROM
tax_rates
INNER JOIN (
country
INNER JOIN (
person
INNER JOIN
person_in_country
ON
person.p_id = person_in_country.p_id
)
ON
country.iso = person_in_country.iso
)
ON
tax_rates.iso = person_in_country.iso
GROUP BY
country.iso,
person.p_id,
person.name_first,
person.name_last,
country.ctry_name,
person_in_country.arrived,
tax_rates.tax_date
HAVING (((tax_rates.tax_date)<=[arrived]))
) as c01
GROUP BY
c01.iso,
c01.p_id,
c01.name_first,
c01.name_last,
c01.ctry_name,
c01.arrived
) as c02
INNER JOIN
tax_rates ON (
c02.mtax_date = tax_rates.tax_date
)
AND
(
c02.iso = tax_rates.iso
);
Output:
iso p_id name_first name_last ctry_name arrived mtax_date tax_rate
GB 1 john smith United Kindom 01/01/1980 01/01/1970 18
FR 2 joanne smyth France 21/03/1987 21/03/1977 15
FR 1 john smith France 17/06/2003 21/03/1994 15
JP 1 john smith Japan 02/07/2008 02/07/2008 18
GB 1 john smith United Kindom 10/01/2009 01/04/2008 15
GB 2 joanne smyth United Kindom 01/10/2008 01/04/2008 15
I'm stuck trying to solve a small part of what is otherwise a complex JOIN.
We have an 'instructions' table and an 'estimates' table. In the 'estimates' we have multiple rows for different types of estimates for a given instruction.
Instructions Table
id | address | status
1 | 27 TAYLOR ROAD, ALBION PARK NSW 2527 | InProgress
Estimates Table
id | instruction_id | basis | basis_date | basis_value
1 | 1 | ContractPrice | 2012-04-05 | 124000
2 | 1 | CAMV | 2012-02-01 | 120000
3 | 1 | CustomerEstimate | 2012-06-07 | 132000
4 | 1 | ContractPrice | 2013-01-03 | 140000
5 | 1 | CustomerEstimate | 2013-02-09 | 145000
What we want is actually 2 joins of 'instructions' on 'estimates' based on instructions.id = estimates.instruction_id and estimates.basis for 1) the most recent 'CustomerEstimate' (aliasing basis_date and basis_value as estimate_date and estimate_value) and 2) most recent 'ContractPrice' (again, aliasing basis_date and basis_value as contact_date and contract_value).
The intended result would be as follows;
id | address | status | contract_price | contract_date | estimate_date | estimate_value
1 | 27 TAYLOR ROAD, ALBION PARK NSW 2527 | InProgress | 2013-01-03 | 140000 | 2013-02-09 | 145000
I would really appreciate some assistance from the SQL gurus out there.
Many thanks,
Trent.
Try
SELECT i.id,
i.address,
i.status,
p.max_date contract_date,
p.basis_value contract_price,
e.max_date estimate_date,
e.basis_value estimate_value
FROM Instructions i LEFT JOIN
(
SELECT q1.instruction_id, max_date, basis_value
FROM Estimates e JOIN
(
SELECT instruction_id, MAX(basis_date) max_date
FROM Estimates
WHERE basis = 'CustomerEstimate'
GROUP BY instruction_id
) q1 ON e.instruction_id = q1.instruction_id AND e.basis_date = q1.max_date
) e ON i.id = e.instruction_id LEFT JOIN
(
SELECT q2.instruction_id, max_date, basis_value
FROM Estimates e JOIN
(
SELECT instruction_id, MAX(basis_date) max_date
FROM Estimates
WHERE basis = 'ContractPrice'
GROUP BY instruction_id
) q2 ON e.instruction_id = q2.instruction_id AND e.basis_date = q2.max_date
) p ON i.id = p.instruction_id
Output:
| ID | ADDRESS | STATUS | CONTRACT_PRICE | CONTRACT_DATE | ESTIMATE_VALUE | ESTIMATE_DATE |
----------------------------------------------------------------------------------------------------------------------------
| 1 | 27 TAYLOR ROAD, ALBION PARK NSW 2527 | InProgress | 140000 | 2013-01-03 | 145000 | 2013-02-09 |
Here is SQLFiddle demo.
What is the contract_price here?
You can try the below
select inst.id
, inst.address
, inst.status
, est.basis_value as estimate_value
, est.basis_date as estimate_date
from instructions inst
, estimates est
where inst.id=est.instruction_id
and (est.basis='CustomerEstimate' or est.basis='ContractPrice')
order
by est.basis
, est.basis_date desc;