I am having hard time to wrap my mind around a multi-level query/sub-query. In short, I a table called REGISTER with 3 fields in it: TransactionId (PK), TransDate (DateTime), Amount (Currency). I need a single query to retrieve the TransactionId where TransDate is the Max and the Amount is the Min qualifying records. For example, if the max of qualifying record returns 5 records with today's date (any future dates is excluded), I would like to know the TransactionId of the lowest Amount within the 5 records returned.
I amble to accomplish this task with two separate query but I am sure it cab be done with a single one.
Query 1 (qryFlag):
SELECT REGISTER.TransDate, REGISTER.*
FROM REGISTER
WHERE (((REGISTER.TransDate)=(
SELECT Max(t2.Transdate) from REGISTER t2
where Transdate <= Date())));
Query 2:
SELECT REGISTER.TransactionId
FROM qryFlag INNER JOIN REGISTER ON qryFlag.TransactionId = REGISTER.TransactionId
WHERE (((qryFlag.Amount)=(SELECT Min(t2.Amount) from qryFlag t2)));
Try:
SELECT TOP 1 VTID.TransactionID
FROM (
SELECT TransactionID, Amount
FROM Register
WHERE TransDate = (SELECT Max(R.TransDate) FROM Register as R WHERE R.TransDate <= Date())
) as VTID
ORDER BY VTID.Amount
HTH
Dale
You need to GROUP BY TransDate to get the min amount and then join to the table:
SELECT r.*
FROM REGISTER AS r INNER JOIN (
SELECT TransDate, MIN(Amount) AS MinAmount
FROM REGISTER
WHERE TransDate = (SELECT Max(Transdate) FROM REGISTER WHERE Transdate <= Date())
GROUP BY TransDate
) AS g on g.TransDate = r.TransDate AND g.MinAmount = r.Amount
Related
Here is my query:
select count(1) from
(select count(1) num, user_id from pos_transactions pt
where date(created_at) <= '2020-6-21'
group by user_id
having num = 1) x
It gives me the number of users who have had 1 transaction until 2020-6-21. Now I want to group it also per date(created_at). I mean, I want to get a list of dates (such as 2020-6-21, 2020-6-22 etc ..) plus the number of users who have had 1 transaction in that date (day).
Any idea how can I do that?
EDIT: The result of query above is correct, the issue is, it's manually now. I mean, I have to increase 2020-6-21 by hand. I want to make it automatically. In other words, I want a list of all dates (from 2020-6-21 til now) contains the number of users who have had 1 transaction until that date.
If you want the number of users who had one transaction on each day, then you need to aggregate by the date as well:
select dte, count(*)
from (select date(created_at) as dte, user_id
from pos_transactions pt
where date(created_at) <= '2020-6-21'
group by dte, user_id
having count(*) = 1
) du
group by dte;
I have these two tables
The first one is expenses table and the second one is expensename
Exp_Type(first table) is the Expense name(second table) as 2 is Food
I am trying to group expense according to expense type and get data between certain dates.
This is what i have tried, but it wont work.
select
(select
(select name from EXPENSENAME where id=EXP_TYPE)as ExpenseType,
sum(PRICE) as cost
from EXPENSES WHERE USERID=1 GROUP BY EXPENSES.EXP_TYPE),
[date]
from EXPENSES where [date] BETWEEN '10-09-2015' and '10-18-2015 23:59:59'
And
select
(select name from EXPENSENAME where id=EXP_TYPE)as ExpenseType,
sum(PRICE) as cost,
date
from EXPENSES WHERE USERID=1 and DATE BETWEEN '01/10/2015' and '29/10/2015' GROUP BY EXPENSES.EXP_TYPE
With out date, i am getting result by this query but i need the same data between certain dates,please help
select
(select name from EXPENSENAME where id=EXP_TYPE)as ExpenseType,
sum(PRICE) as cost
from EXPENSES WHERE USERID=1 GROUP BY EXPENSES.EXP_TYPE
you want to join the tables together
SELECT en.name as ExpenseType, SUM(e.price) as cost
FROM expenses e
JOIN expensename en ON en.id = e.exp_type
WHERE e.date BETWEEN '10-09-2015' and '10-18-2015'
GROUP BY en.name
this should give you the cost per name
the current query you have is TERRIBLE... and this is why
SELECT (SELECT ... FROM ... WHERE ... ) as ...
this is creating a correlated subquery which is executing once for every row of the parent select. meaning if you have a table with 4 rows in it (SELECT ... FROM ... WHERE ... ) will execute 4 times scanning 16 rows (assuming its from the same table) in general that is a really really bad way to get data... if you have a million rows... well do the math, its a bad idea
I have two tables, one is a list of firms, the other is a list of jobs the firms have advertised with deadlines for application and start dates.
Some of the firms will have advertised no jobs, some will only have jobs that are past their deadline dates, some will only have live jobs and others will have past and live applications.
What I want to be able to show as a result of a query is a list of all the firms, with the nearest deadline they have, sorted by that deadline. So the result might look something like this (if today was 2015-01-01).
Sorry, I misstated that. What I want to be able to do is find the next future deadline, and if there is no future deadline then show the last past deadline. So in the first table below the BillyCo deadline has passed, but the next BuffyCo deadline is shown. In the BillyCo case there are earlier deadlines, but in the BuffyCo case there are both earlier and later deadlines.
id name title date
== ==== ===== ====
1 BobCo null null
2 BillCo Designer 2014-12-01
3 BuffyCo Admin 2015-01-31
So, BobCo has no jobs listed at all, BillCo has a deadline that has passed and BuffyCo has a deadline in the future.
The problematic part is that BillCo may have a set of jobs like this:
id title date desired hit
== ===== ==== ===========
1 Coder 2013-12-01
2 Manager 2014-06-30
3 Designer 2012-12-01 <--
And BuffyCo might have:
id title date desired hit
== ===== ==== ===========
1 Magician 2013-10-01
2 Teaboy 2014-05-19
3 Admin 2015-01-31 <--
4 Writer 2015-02-28
So, I can do something like:
select * from (
select * from firms
left join jobs on firms.id = jobs.firmid
order by date desc)
as t1 group by firmid;
Or, limit the jobs joined or returned by a date criterion, but I don't seem to be able to get the records I want returned. ie the above query would return:
id name title date
== ==== ===== ====
1 BobCo null null
2 BillCo Designer 2014-12-01
3 BuffyCo Writer 2015-02-28
For BuffyCo it's returning the Writer job rather than the Admin job.
Is it impossible with an SQL query? Any advice appreciated, thanks in advance.
I think this may be what you need, you need:
1) calculate the delta for all of your jobs between the date and the current date finding the min delta for each firm.
2) join firms to jobs only on where firm id's match and where the calculated min delta for the firm matches the delta for the row in jobs.
SELECT f.id, f.name, j.title,j.date
FROM firms f LEFT JOIN
(SELECT firmid,MIN(abs(datediff(date, curdate())))) AS delta
FROM jobs
GROUP BY firmid) d
ON f.id = d.firmid
LEFT JOIN jobs j ON f.id = j.id AND d.delta = abs(datediff(j.date, curdate())))) ;
You want to make an outer join with something akin to the group-wise maximum of (next upcoming, last expired):
SELECT * FROM firms LEFT JOIN (
-- fetch the "groupwise" record
jobs NATURAL JOIN (
-- using the relevant date for each firm
SELECT firmid, MAX(closest_date) date
FROM (
-- next upcoming deadline
SELECT firmid, MIN(date) closest_date
FROM jobs
WHERE date >= CURRENT_DATE
GROUP BY firmid
UNION ALL
-- most recent expired deadline
SELECT firmid, MAX(date)
FROM jobs
WHERE date < CURRENT_DATE
GROUP BY firmid
) closest_dates
GROUP BY firmid
) selected_dates
) ON jobs.firmid = firms.id
This will actually give you all jobs that have the best deadline date for each firm. If you want to restrict the results to an indeterminate record from each such group, you can add GROUP BY firms.id to the very end.
The revision to your question makes it rather trickier, but it can still be done. Try this:
select
closest_job.*, firm.name
from
firms
left join (
select future_job.*
from
(
select firmid, min(date) as mindate
from jobs
where date >= curdate()
group by firmid
) future
inner join jobs future_job
on future_job.firmid = future.firmid and future_job.date = future.mindate
union all
select past_job.*
from
(
select firmid, max(date) as maxdate
from jobs
group by firmid
having max(date) < curdate()
) past
inner join jobs past_job
on past_job.firmid = past.firmid and past_job.date = past.maxdate
) closest_job
on firms.id = closest_job.firmid
I think this does what I need:
select * from (
select firms.name, t2.closest_date from firms
left join
(
select * from (
--get first date in the future
SELECT firmid, MIN(date) closest_date
FROM jobs
WHERE date >= CURRENT_DATE
GROUP BY firmid
UNION ALL
-- most recent expired deadline
SELECT firmid, MAX(date)
FROM jobs
WHERE date < CURRENT_DATE
GROUP BY firmid) as t1
-- order so latest date is first
order by closest_date desc) as t2
on firms.id = t2.firmid
-- group by eliminates all but latest date
group by firms.id) as t3
order by closest_date asc;
Thanks for all the help on this
I have the following data table.
Record Date Price
A 3/1/2015 5
A 3/2/2015 6
A 3/3/2015 7
A 3/4/2015 10
B 2/1/2015 4
B 2/2/2015 6
B 2/3/2015 15
B 2/4/2015 2
How can I output a table that only shows the First price and the last price for each record for the first date in the table and the last date in the table. Output columns would be Record, First Price, Last Price. I am looking for a one step solution that is easy to implement in order to create a custom view.
The output desired would be:
Record FirstPrice LastPrice
A 5 10
B 4 2
Perhaps something like this is what you are looking for?
select R.Record, FD.Price as MinPrice, LD.Price as MaxPrice
from Records R
join (
select Price, R1.Record
from Records R1
where Date = (select MIN(DATE) from Records R2 where R2.Record = R1.Record)
) FD on FD.Record = R.Record
join (
select Price, R1.Record
from Records R1
where Date = (select MAX(DATE) from Records R2 where R2.Record = R1.Record)
) LD on LD.Record = R.Record
group by R.Record
http://sqlfiddle.com/#!9/d047b/26
Get the min and max aggregate dates grouped by the record field and join back to the root data. If you can have multiple records for the same record field on the same date, you will have to use min, max or avg to get just one value for that date.
SQLFiddle: http://sqlfiddle.com/#!9/1158b/3
SELECT anchorData.Record
, firstRecord.Price
, lastRecord.Price
FROM (
SELECT Record
, MIN(Date) AS FirstDate
, MAX(Date) AS LastDate
FROM Table1
GROUP BY Record
) AS anchorData
JOIN Table1 AS firstRecord
ON firstRecord.Record = anchorData.Record
AND firstRecord.Date = anchorData.FirstDate
JOIN Table1 AS lastRecord
ON lastRecord.Record = anchorData.Record
AND lastRecord.Date = anchorData.LastDate
"in order to create a custom view."...are you looking to do this in Oracle/MySql as a CREATE VIEW or just a query/select statement?
I have a table representing a car traveling a route, broken up into segments:
CREATE TABLE travels (
id int auto_increment primary key,
segmentID int,
month int,
year int,
avgSpeed int
);
-- sample data
INSERT INTO travels (segmentID, month, year, avgSpeed)
VALUES
(15,1,2014,80),
(15,1,2014,84),
(15,1,2014,82),
(15,2,2014,70),
(15,2,2014,68),
(15,2,2014,66);
The above schema and sample data is also available as a fiddle.
What query will identify segment IDs where average driving speed decreased by more than 10% compared to the previous month?
here is my solution
Sqlfidle demo
The key is to keep track between previous month and next so i`m doing year*100+month and after group by year and moth check for difference 1 and 89 in year*100+month field.
Also it is pitty that MySQL does not support CTE and makes query ugly using derivered tables.
Code:
select s.month,s.speed,m.month as prevmonth,m.speed as sp, 100-s.speed/m.speed*100 as speeddiff from
(SELECT segmentid,month,year*100+month as mark,avg(avgSpeed) as speed from travels
group by segmentid,month,year*100+month
) as s
,
(SELECT segmentid,month,year*100+month as mark,avg(avgSpeed) as speed from travels
group by segmentid,month,year*100+month
) as m
where s.segmentid=m.segmentid and (s.mark=m.mark+1 or s.mark=m.mark+89) and (m.speed-(m.speed/10))>s.speed;
CTE code working on every DB except MySQL
with t as(SELECT segmentid,month,year*100+month as mark,avg(avgSpeed) as speed from travels
group by segmentid,month,year*100+month
)
select s.month,s.speed,m.month as prevmonth,m.speed as sp, 100-s.speed/m.speed*100 as speeddiff from t s
inner join t m on s.segmentid=m.segmentid and (s.mark=m.mark+1 or s.mark=m.mark+89)
where (m.speed-(m.speed/10))>s.speed;
You need to select each month, join the next month (which is a little convoluted due to your table structure) and find the decrease(/increase). Try the following complex query
SELECT
t1.segmentID, t1.month, t1.year, AVG(t1.avgSpeed) as avgSpeed1,
AVG(t2.avgSpeed) as avgSpeed2,
1-(AVG(t1.avgSpeed)/AVG(t2.avgSpeed)) as decrease
FROM
travels t1
LEFT JOIN
travels t2
ON
CONCAT(t2.year,'-',LPAD(t2.month,2,'00'),'-',LPAD(1,2,'00')) = DATE_ADD(CONCAT(t1.year,'-',LPAD(t1.month,2,'00'),'-',LPAD(1,2,'00')), INTERVAL -1 MONTH)
GROUP BY
segmentID, month, year
HAVING
avgSpeed1/avgSpeed2 < .9
Here is the updated SQLFiddle - http://sqlfiddle.com/#!2/183c1/25
This requires a self join. This answer will get you started. You can work on the details.
select somefields
from yourtable t1 join yourtable t2 on t1.something = t2.something
where t1.month = whatever
and t2.month = t1.month + 1
and t2.speed <= t1.speed * .9