MySQL - Derived table query not fetching data for nested Select query - mysql

I have a quey which has growth rates over a period of time. I am trying to obtain the overall growth between two rows that I specify. Here is the SQL fiddle http://sqlfiddle.com/#!9/1756ca/2
select i1.Month, i1.Rate, EXP(SUM(LOG(1+i2.Rate))) InfRate
from Inflation i1
inner join Inflation i2 on i1.Month >=i2.Month
group by i1.Month, i1.Rate
order by Month DESC
This seems to work correctly and I am able to get the growth rate for the entire Month range in the fiddle, however I am trying to use a derived table so that I can specify the Month period, like this, however it is not working
select i1.Month, i1.Rate, EXP(SUM(LOG(1+i2.Rate))) InfRate
from (SELECT * FROM `Inflation` WHERE Month between '2020-01-01' and '2022-01-01') as DT
inner join Inflation i2 on i1.Month >=i2.Month
group by i1.Month, i1.Rate
order by Month DESC
I get the error #1054 - Unknown column 'i1.Month' in 'field list'
I am trying to use a derived table for the period between '2020-01-01' and '2022-01-01' or any other range that I specify, however it does not seem to be working for me. Any help will be appreciated.
The expected result is something like this, considering that only the period between '2020-01-01' and '2022-01-01' was queried
http://sqlfiddle.com/#!9/13f818/1
There seems to be some problem with the fiddle, here is an updated one
https://dbfiddle.uk/TwQ7VWs2

If you are looking for "inflation since the start of 2020, you need to limit i2:
Select DT.Month, DT.Rate, EXP(SUM(LOG(1+i2.Rate))) InfRate
from (SELECT * FROM `Inflation` WHERE Month between '2020-01-01' and '2022-06-01') as DT
inner join Inflation i2 on DT.Month >=i2.Month
where i2.Month >= '2020-01-01' -- I added this
group by DT.Month, DT.Rate
order by Month DESC;

You don't have a table named i1 in your second example, yet you're querying values from it. I believe what you want is something like
select DT.Month, DT.Rate, EXP(SUM(LOG(1+i2.Rate))) InfRate
from (SELECT * FROM `Inflation` WHERE Month between '2020-01-01' and '2022-01-01') as DT
inner join Inflation i2 on DT.Month >=i2.Month
group by DT.Month, DT.Rate
order by Month DESC
Which replaces your i1 references with DT.

Related

Mysql. how to join two tables from this example?

everybody.
I have two requests.
1 query - show the list of dates with time 22:00 from one table
SELECT DATE_FORMAT(tt.create_time,"%Y-%m-%d 22:00:00") AS DAY,tt.id
FROM tick tt
GROUP BY DATE_FORMAT(tt.create_time,"%Y-%m-%d")
2 query - shows the number of records that have create_time less than the date specified in the query
SELECT COUNT(*) AS count FROM
(SELECT * FROM
(SELECT * FROM tick_history th
WHERE th.create_time < '2019-04-15 22:00:00'
ORDER BY th.id DESC) AS t1
GROUP BY t1.tick_id) AS t2
WHERE t2.state NOT IN (1,4,9) AND t2.queue = 1
Is it possible to somehow combine these two queries to get one column with dates from the first query, and the second column is the number from the second query for each date from the first column?
Ie as if substituted date and calculated the number of the second request..
Is it possible? Help with request please

Select query with calculated field that uses subquery with multiple conditions

Three Tables:
COST_SAVINGS: COST_SAVINGS_ID, ONE_TIME_CREDIT, CREATION_DATE, INVOICE_ID (FK to Invoice table)
INVOICE: INVOICE_ID, INVOICE_CURRENCY_CODE
EXCHANGE_RATE: CURRENCY_RATE, CURRENCY_DATE
I'm reporting on Cost Savings (the first table). The challenge is that each cost savings amount can be in a different currency so I need a field that shows the converted amount based on the currency from the invoice table and a matching month / year between the Exchange.Ex_Date and Cost_Savings.Create_Date.
I'm getting an error that states:
single-row subquery returns more than one row
This is what I have so far:
SELECT
COST_SAVINGS.COST_SAVINGS_ID,
COST_SAVINGS.CLAIM_TYPE,
COST_SAVINGS.COMMENTS,
COST_SAVINGS.COST_SAVINGS_STATUS,
COST_SAVINGS.CREATION_DATE,
COST_SAVINGS.DESCRIPTION,
COST_SAVINGS.ONE_TIME_CREDIT AS CREDIT_IN_NATIVE_CURRENCY,
FINANCE_INVOICE.CURRENCY_CODE,
COST_SAVINGS.ONE_TIME_CREDIT *
(SELECT EXCHANGE_RATE.CURRENCY_RATE
FROM EXCHANGE_RATE
WHERE EXTRACT (MONTH FROM COST_SAVINGS.CREATION_DATE) = EXTRACT (MONTH FROM EXCHANGE_RATE.CURRENCY_DATE)
AND EXTRACT (YEAR FROM COST_SAVINGS.CREATION_DATE) = EXTRACT (YEAR FROM EXCHANGE_RATE.CURRENCY_DATE)
AND FINANCE_INVOICE.CURRENCY_CODE = EXCHANGE_RATE.CURRENCY_CODE) AS CREDIT_IN_USD
FROM COST_SAVINGS
LEFT JOIN FINANCE_INVOICE ON COST_SAVINGS.INVOICE_ID = FINANCE_INVOICE.INVOICE_ID
I feel like the issue may be with the third WHERE clause in my subquery (Trying to match the Currency codes). I'm not sure how resolve it though. Any thoughts?
Try putting LIMIT 1 in your subquery:
...RENCY_CODE = EXCHANGE_RATE.CURRENCY_CODE LIMIT 1) AS CREDIT_IN_USD
I believe your subquery is returning multiple rows. That doesn't work when you use a subquery in place of a column name in your SELECT clause.
If you don't want to use a subquery in place of a column name, try something like this. You'll join to a subquery generated virtual table.
Here's the virtual table for exchange rates. It uses GROUP BY to make one row (or no rows per month/year/currency code. If more than one row is in the raw table for any month/year/code, they get averaged with AVG(). You could also use MAX() or MIN().
SELECT AVG(CURRENCY_RATE) CURRENCY_RATE,
CURRENCY_CODE,
EXTRACT(MONTH FROM CURRENCY_DATE) MONTH,
EXTRACT(YEAR FROM CURRENCY_DATE) YEAR
FROM EXCHANGE_RATE
GROUP BY CURRENCY_CODE,
EXTRACT(MONTH FROM CURRENCY_DATE),
EXTRACT(YEAR FROM CURRENCY_DATE)
Try this and convince yourself it works.
Then build it into your overall query.
SELECT
COST_SAVINGS.COST_SAVINGS_ID,
COST_SAVINGS.whatever, ...
FINANCE_INVOICE.CURRENCY_CODE,
(COST_SAVINGS.ONE_TIME_CREDIT * RATE.CURRENCY_RATE) AS CREDIT_IN_USD
FROM COST_SAVINGS
LEFT JOIN FINANCE_INVOICE ON COST_SAVINGS.INVOICE_ID = FINANCE_INVOICE.INVOICE_ID
LEFT JOIN (SELECT AVG(CURRENCY_RATE) CURRENCY_RATE,
CURRENCY_CODE,
EXTRACT(MONTH FROM CURRENCY_DATE) MONTH,
EXTRACT(YEAR FROM CURRENCY_DATE) YEAR
FROM EXCHANGE_RATE
GROUP BY CURRENCY_CODE,
EXTRACT(MONTH FROM CURRENCY_DATE),
EXTRACT(YEAR FROM CURRENCY_DATE)
) RATE ON RATE.CURRENCY_CODE = FINANCE_INVOICE.CURRENCY_CODE
AND RATE.YEAR = EXTRACT(YEAR FROM COST_SAVINGS.CREATION_DATE)
AND RATE.MONTH = EXTRACT(MONTH FROM COST_SAVINGS.CREATION_DATE)

MySQL Group Grouped By Result

I have a very simple table which consists of the following columns:
id | customer_id | total | created_at
I was running this query to get the results per day for the last ten days:
SELECT SUM(total) AS total, DATE_FORMAT(created_at, "%d/%m/%Y") AS date
FROM table
WHERE created_at BETWEEN "2017-02-20" AND "2017-03-01"
GROUP BY created_at
ORDER BY created_at DESC
This works fine, but I've just noticed that there's an issue with imported rows being duplicated for some reason so I'd like to update the query to be able to handle the situation if it ever happens again, in other words select one row instead of all when the date and customer id are the same (the total is also identical).
If I add customer_id to the group by that seems to work but the trouble with that is then the query returns a result per day for each customer when I only want the overall total.
I've tried a couple of things but I haven't cracked it yet, I think it will be achievable using a sub query and/or an inner join, I have tried this so far but the figures are very wrong:
SELECT
created_at,
(
SELECT SUM(total)
FROM table test
WHERE test.created_at = table.created_at
AND test.customer_id = table.customer_id
GROUP BY customer_id, created_at
LIMIT 1
) AS total
FROM table
WHERE created_at BETWEEN "2017-02-20" AND "2017-03-01"
GROUP BY created_at
ORDER BY created_at DESC
It's also a large table so finding a performant way to do this is also important.
First, are you sure that created_at is a date and not a datetime? This makes a big difference.
You can do what you want using two levels of aggregation:
SELECT SUM(max_total) AS total, DATE_FORMAT(created_at, '%d/%m/%Y') AS date
FROM (SELECT t.customer_id, t.created_at, MAX(total) as max_total
FROM table t
WHERE t.created_at BETWEEN '2017-02-20' AND '2017-03-01'
GROUP BY t.customer_id, t.created_at
) t
GROUP BY created_at
ORDER BY created_at DESC;

Looping over a single column in mysql

I have a table that looks like this and i want to know the number of entries that are registered over a six hour time period and display that period which has max number of entries.
Time
09:42:29
10:37:28
15:18:49
15:28:34
16:43:51
18:14:10
18:26:06
18:26:14
So for each element in Time column, i will include a 6 hour period starting from that element and count how many entries in that column will fall in that period.
Ex 09:42:29 will have the end period has 15:42:29 and it should have count as 4 (09:42:29,10:37:28
15:18:49,15:28:34).
So do this for each element in Time Column and whichever element has max count, that will be the starting time of the period and display the start and end period accordingly.
Help me with writing a mysql query for this. Thank You!!!
Hope it helps
select
T.TimeStart,
T.TimeEnd,
COUNT(*)
from (
select
T.Time TimeStart,
date_add(T.Time,INTERVAL 6 HOUR) TimeEnd
from TimeTable T
) T
inner join TimeTable T2 on
T2.Time between T.TimeStart and T.TimeEnd
group by
T.TimeStart,
T.TimeEnd
The code below is for MSSQL but it works as expected and should give you some guidelines how the example above could be used
WITH TimeTable([Time]) AS (
select
CONVERT(DATETIME,a.a)
from (
values
('09:42:29'),
('10:37:28'),
('15:18:49'),
('15:28:34'),
('16:43:51'),
('18:14:10'),
('18:26:06'),
('18:26:14'))a(a)
)
select
convert(time(7),T.TimeStart)TimeStart,
convert(time(7),T.TimeEnd)TimeEnd,
COUNT(*) [Ocorrences]
from (
select
T.Time TimeStart,
DATEADD(HOUR,6,T.Time) TimeEnd
from TimeTable T
) T
inner join TimeTable T2 on
T2.Time between T.TimeStart and T.TimeEnd
group by
T.TimeStart,
T.TimeEnd

ORDER BY date and time BEFORE GROUP BY name in mysql

i have a table like this:
name date time
tom | 2011-07-04 | 01:09:52
tom | 2011-07-04 | 01:09:52
mad | 2011-07-04 | 02:10:53
mad | 2009-06-03 | 00:01:01
i want oldest name first:
SELECT *
ORDER BY date ASC, time ASC
GROUP BY name
(->doesn't work!)
now it should give me first mad(has earlier date) then tom
but with GROUP BY name ORDER BY date ASC, time ASC gives me the newer mad first because it groups before it sorts!
again: the problem is that i can't sort by date and time before i group because GROUP BY must be before ORDER BY!
Another method:
SELECT *
FROM (
SELECT * FROM table_name
ORDER BY date ASC, time ASC
) AS sub
GROUP BY name
GROUP BY groups on the first matching result it hits. If that first matching hit happens to be the one you want then everything should work as expected.
I prefer this method as the subquery makes logical sense rather than peppering it with other conditions.
As I am not allowed to comment on user1908688's answer, here a hint for MariaDB users:
SELECT *
FROM (
SELECT *
ORDER BY date ASC, time ASC
LIMIT 18446744073709551615
) AS sub
GROUP BY sub.name
https://mariadb.com/kb/en/mariadb/why-is-order-by-in-a-from-subquery-ignored/
I think this is what you are seeking :
SELECT name, min(date)
FROM myTable
GROUP BY name
ORDER BY min(date)
For the time, you have to make a mysql date via STR_TO_DATE :
STR_TO_DATE(date + ' ' + time, '%Y-%m-%d %h:%i:%s')
So :
SELECT name, min(STR_TO_DATE(date + ' ' + time, '%Y-%m-%d %h:%i:%s'))
FROM myTable
GROUP BY name
ORDER BY min(STR_TO_DATE(date + ' ' + time, '%Y-%m-%d %h:%i:%s'))
This worked for me:
SELECT *
FROM your_table
WHERE id IN (
SELECT MAX(id)
FROM your_table
GROUP BY name
);
Use a subselect:
select name, date, time
from mytable main
where date + time = (select min(date + time) from mytable where name = main.mytable)
order by date + time;
If you wont sort by max date and group by name, you can do this query:
SELECT name,MAX(date) FROM table group by name ORDER BY name
where date may by some date or date time string. It`s response to you max value of date by each one name
Another way to solve this would be with a LEFT JOIN, which could be more efficient. I'll first start with an example that considers only the date field, as probably it is more common to store date + time in one datetime column, and I also want to keep the query simple so it's easier to understand.
So, with this particular example, if you want to show the oldest record based on the date column, and assuming that your table name is called people you can use the following query:
SELECT p.* FROM people p
LEFT JOIN people p2 ON p.name = p2.name AND p.date > p2.date
WHERE p2.date is NULL
GROUP BY p.name
What the LEFT JOIN does, is when the p.date column is at its minimum value, there will be no p2.date with a smaller value on the left join and therefore the corresponding p2.date will be NULL. So, by adding WHERE p2.date is NULL, we make sure to show only the records with the oldest date.
And similarly, if you want to show the newest record instead, you can just change the comparison operator in the LEFT JOIN:
SELECT p.* FROM people p
LEFT JOIN people p2 ON p.name = p2.name AND p.date < p2.date
WHERE p2.date is NULL
GROUP BY p.name
Now, for this particular example where date+time are separate columns, you would need to add them in some way if you want to query based on the datetime of two columns combined, for example:
SELECT p.* FROM people p
LEFT JOIN people p2 ON p.name = p2.name AND p.date + INTERVAL TIME_TO_SEC(p.time) SECOND > p2.date + INTERVAL TIME_TO_SEC(p2.time) SECOND
WHERE p2.date is NULL
GROUP BY p.name
You can read more about this (and also see some other ways to accomplish this) on the The Rows Holding the Group-wise Maximum of a Certain Column page.
I had a different variation on this question where I only had a single DATETIME field and needed a limit after a group by or distinct after sorting descending based on the datetime field, but this is what helped me:
select distinct (column) from
(select column from database.table
order by date_column DESC) as hist limit 10
In this instance with the split fields, if you can sort on a concat, then you might be able to get away with something like:
select name,date,time from
(select name from table order by concat(date,' ',time) ASC)
as sorted
Then if you wanted to limit you would simply add your limit statement to the end:
select name,date,time from
(select name from table order by concat(date,' ',time) ASC)
as sorted limit 10
In Oracle, This work for me
SELECT name, min(date), min(time)
FROM table_name
GROUP BY name
work for me mysql
select * from (SELECT number,max(date_added) as datea FROM sms_chat group by number) as sup order by datea desc
This is not the exact answer, but this might be helpful for the people looking to solve some problem with the approach of ordering row before group by in mysql.
I came to this thread, when I wanted to find the latest row(which is order by date desc but get the only one result for a particular column type, which is group by column name).
One other approach to solve such problem is to make use of aggregation.
So, we can let the query run as usual, which sorted asc and introduce new field as max(doc) as latest_doc, which will give the latest date, with grouped by the same column.
Suppose, you want to find the data of a particular column now and max aggregation cannot be done.
In general, to finding the data of a particular column, you can make use of GROUP_CONCAT aggregator, with some unique separator which can't be present in that column, like GROUP_CONCAT(string SEPARATOR ' ') as new_column, and while you're accessing it, you can split/explode the new_column field.
Again, this might not sound to everyone. I did it, and liked it as well because I had written few functions and I couldn't run subqueries. I am working on codeigniter framework for php.
Not sure of the complexity as well, may be someone can put some light on that.
Regards :)