Get Maximum value column from subquery mysql - mysql

i have a mysql query as
SELECT date,shortcode,SUM(count) as myCount
FROM mytable
WHERE smsc='123'
AND username NOT REGEXP '[A-Za-z]+'
AND DATE(date) >= CURDATE() - INTERVAL 7 DAY
GROUP BY shortcode,date
ORDER BY date ASC
Which gives the result as following which is perfectly fine
date
shortcode
myCount
2021-02-18
123
7
2021-02-18
231
15
2021-02-19
783
117
2021-02-19
894
115
2021-02-20
009
70
2021-02-20
565
15
now what i want to do is get max value of maxcount from each day and corresponding shortcode so what i did is
Select date,Max(myCount) as maxim,shortcode
from ( SELECT date,shortcode,SUM(count) as myCount
FROM mytable
WHERE smsc='123'
AND username NOT REGEXP '[A-Za-z]+'
AND DATE(date) >= CURDATE() - INTERVAL 7 DAY
GROUP BY shortcode,date
ORDER BY date ASC ) as a
GROUP BY a.date;
which is not giving me the right result
my desired result is
date | shortcode | maxim
2021-02-18 | 231 |15
2021-02-19 | 783 |117
2020-02-20 | 009 |70

You can use common table expression and row_number() to achieve your desired result:
with cte as (
select date,shortcode,mycount,row_number()over(partition by date order by mycount desc) rn from (SELECT date,shortcode,SUM(count) as myCount
FROM mytable
WHERE smsc='123'
AND username NOT REGEXP '[A-Za-z]+'
AND DATE(date) >= CURDATE() - INTERVAL 7 DAY
GROUP BY shortcode,date )t)
select date,shortcode,mycount from cte where rn=1
This query will generate a sequence for each date orderring by mycount in descendent order. So always the first row for each date will contain the max mycount.

If you can use a variable, the query is:
SET #x='';
SELECT
#x:=date,shortcode,myCount FROM (
SELECT date,shortcode,SUM(count) as myCount
FROM mytable
WHERE smsc='123'
AND username NOT REGEXP '[A-Za-z]+'
AND DATE(date) >= CURDATE() - INTERVAL 7 DAY
GROUP BY shortcode,date
ORDER BY date ASC,myCount DESC
) a
WHERE #x<>date
;
Fiddle

Related

Show number of lower and higher values in SQL

I have a certain problem while trying to make an SQL. I have a table with the following format and data.
id
value
date
12
3
2020-06-01
12
4
2020-06-09
12
1
2020-06-20
5
4
2020-06-11
5
5
2020-06-17
My goal is to make something like that:
id
lower
higher
12
1
1
5
0
1
This looks for the value of the oldest row IN specific interval (ex. 100 days)and it compares it with all dates after that if their values are higher and lower and return the count.
I do have something that works but it requires more queries:
One to group take all ids with dates in the interval of xx days
SELECT id FROM table
WHERE date >= CURDATE() - INTERVAL 30 DAY GROUP BY id
ORDER BY id ASC;
And then I loop through each row and get its lower and higher values.
SELECT
*
FROM
(
SELECT
COUNT(*) AS higher, id
FROM
`table`
WHERE
id = 12 AND date > CURDATE() - INTERVAL 30 DAY AND value > (
SELECT value FROM table
WHERE table.date >= CURDATE() - INTERVAL 30 DAY AND id = 12
ORDER BY `table`.`date` ASC LIMIT 1
)
) AS t1,(
SELECT
COUNT(*) AS deteriorated_placements
FROM
`table`
WHERE
id = 12 AND date > CURDATE() - INTERVAL 30 DAY AND value < (
SELECT value FROM table
WHERE table.date >= CURDATE() - INTERVAL 30 DAY AND id = 12
ORDER BY `table`.`date` ASC LIMIT 1
)
) AS t2;
The problem with that is that I do around 40 more queries. I know it maybe is not a big issue but
Is there a way to somehow combine those 2 queries?
Use first_value():
select id,
sum(value < value_1) as lower,
sum(value > value_1) as higher
from (select t.*,
first_value(value) over (partition by id order by date) as value_1
from t
) t
group by id;

Query to subtract same column value at different interval of day with SQL database

In MySQL, I want to subtract one of column value at different interval of time based on another column 'timestamp'.
table structure is :
id | generator_id | timestamp | generated_value
1 | 1 | 2019-05-27 06:55:20 | 123456
2 | 1 | 2019-05-27 07:55:20 | 234566
3 | 1 | 2019-05-27 08:55:20 | 333456
..
..
20 | 1 | 2019-05-27 19:55:20 | 9876908
From above table I want to fetch the generated_value column value which should be difference of first timestamp fo day and timestamp of last value of day.
In above example I am looking query which should give me output as 9,753,452 (9876908 - 123456).
In general to fetch the single record of first value and last value of day I use below query
// Below will give me end day value
SELECT * FROM generator_meters where generator_id=1 and timestamp like '2019-05-27%' order by timestamp desc limit 1 ;
//this will give me last day value
SELECT * FROM generator_meters where generator_id=1 and timestamp like '2019-05-27%' order by timestamp limit 1 ;
Question is how should I get the final generated_value by doing minus of first value of day from last value of day.
Expected Output
generator_id | generated_value
1 | 9753452
Thanks in advance !!
In your example the value gets bigger and bigger. If this is guaranteed to be so, you can use
select max(generated_value) - min(generated_value) as result
from sun_electric.generator_meters
where generator_id = 1
and date(timestamp) = date '2019-05-27';
Or for multiple IDs:
select generator_id, max(generated_value) - min(generated_value) as result
from sun_electric.generator_meters
and date(timestamp) = date '2019-05-27'
group by generator_id
order by generator_id;
If the value is not ascending, then you can use the following query for ID 1:
select last_row.generated_value - first_row.generated_value as result
from
(
select *
from sun_electric.generator_meters
where generator_id = 1
and date(timestamp) = date '2019-05-27'
order by timestamp
limit 1
) first_row
cross join
(
select *
from sun_electric.generator_meters
where generator_id = 1
and date(timestamp) = date '2019-05-27'
order by timestamp desc
limit 1
) last_row;
Here is one way to get a result for multiple IDs:
select
minmax.generator_id,
(
select generated_value
from sun_electric.generator_meters gm
where gm.generator_id = minmax.generator_id
and gm.timestamp = minmax.max_ts
) -
(
select generated_value
from sun_electric.generator_meters gm
where gm.generator_id = minmax.generator_id
and gm.timestamp = minmax.min_ts
) as result
from
(
select generator_id, min(timestamp) as min_ts, max(timestamp) as max_ts
from sun_electric.generator_meters
where date(timestamp) = date '2019-05-27'
group by generator_id
) minmax
order by minmax.generator_id;
You can also move the subqueries to the from clause and join them, if you like this better. Yet another approach would be to use window functions, available as of MySQL 8.
This following script will return your expected results for the filtered ID and Date-
SELECT generator_id,CAST(timestamp AS DATE) ,
(
SELECT generated_value
FROM sun_electric.generator_meters B
WHERE timestamp = max(timestamp)
)
-
(
SELECT generated_value
FROM sun_electric.generator_meters B
WHERE timestamp = min(timestamp)
) AS Diff
FROM sun_electric.generator_meters
WHERE generator_id = 1
AND CAST(timestamp AS DATE) = '2019-05-27'
GROUP BY generator_id,CAST(timestamp AS DATE) ;
If you want the same result with GROUP BY ID and Date just remove the filter as below-
SELECT generator_id,CAST(timestamp AS DATE) ,
(
SELECT generated_value
FROM sun_electric.generator_meters B
WHERE timestamp = max(timestamp)
)
-
(
SELECT generated_value
FROM sun_electric.generator_meters B
WHERE timestamp = min(timestamp)
) AS Diff
FROM sun_electric.generator_meters
GROUP BY generator_id,CAST(timestamp AS DATE) ;

Count records which fulfil given condition

I have a table with the following schema:
+-------------------------------------------------------+
| table_counter |
+----+---------------------+------------+---------------+
| id | timestamp | entry_type | country |
+----+---------------------+------------+---------------+
+----+---------------------+------------+---------------+
| 10 | 2017-05-01 12:00:00 | click | Germany |
+----+---------------------+------------+---------------+
| 11 | 2017-05-01 12:00:00 | view | Austria |
+----+---------------------+------------+---------------+
| 12 | 2017-05-01 12:00:00 | click | UK |
+----+---------------------+------------+---------------+
| 13 | 2017-05-01 12:00:00 | view | USA |
+----+---------------------+------------+---------------+
I need to return the following result: Select the sum of views and clicks of the top 5 countries by sum of views in the past 30 days.
I know how to count the records all right, but how do I define the constrains? How do I return all entries from five countries with the highest number of views?
Limiting the result to the last 30 days is trivial, but I'm pretty much stuck at the beginning.
Using order by and limit keywords,
SELECT SUM(IF(entry_type = "view", 1, 0)) as view_count FROM t3 GROUP BY country, entry_type ORDER BY view_count DESC LIMIT 5
--EDIT
As per the requirement stated in the comments, here's the updated query:
SELECT SUM(view_click_count) as all_total FROM (SELECT country, SUM(IF(entry_type = "view", 1, 0)) as view_count, SUM(IF(entry_type = "click", 1, 0)) as click_count, count(entry_type) as view_click_count FROM t3 GROUP BY country ORDER BY view_count DESC LIMIT 5) t2
all_total gives the total count as needed, for top 5 countries.
You can do it this way:
select
tc.country,
count(case entry_type when 'click' then 1 else null end) clicks,
count(case entry_type when 'view' then 1 else null end) views
from table_counter tc
inner join (
select top 5 country from [dbo].[table_counter]
where entry_type = 'view'
and timestamp >= DATEADD(DAY, -30, GETDATE())
group by country
order by count(entry_type) desc
) t on t.country = tc.country
where timestamp >= DATEADD(DAY, -30, GETDATE())
group by tc.country
order by views desc
This is for SQL Server. A few tweaks might be needed for MySQL (i.e. 'Limit' instead of 'TOP')
You can get top 5 countries by views with the following query, e.g.:
SELECT country, count(*) as 'views'
FROM table
WHERE timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()
AND entry_type = 'view'
GROUP BY country
ORDER BY count(*) DESC
LIMIT 5
Now, to select clicks, you can add another query in SELECT , e.g.:
SELECT t.country, COUNT(*) as 'views',
(SELECT COUNT(*)
FROM `table`
WHERE country = t.country
AND entry_type = 'click'
AND timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()
) as 'clicks'
FROM `table` t
WHERE t.timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()
AND t.entry_type = 'view'
GROUP BY t.country
ORDER BY count(*) DESC
LIMIT 5
Here's the SQL Fiddle.
Update
To get the SUM of views and clicks, wrap the above query into another SELECT, e.g.:
SELECT country, views + clicks
FROM(
SELECT t.country, COUNT(*) as 'views',
(SELECT COUNT(*)
FROM `table`
WHERE country = t.country
AND entry_type = 'click'
AND timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()
) as 'clicks'
FROM `table` t
WHERE t.timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()
AND t.entry_type = 'view'
GROUP BY t.country
ORDER BY count(*) DESC
LIMIT 5
) b;
Here's the updated SQL Fiddle.

Change MySql value based on time that has past

I have the following table:
user_id post_streak streak_date streak first_name club_id
-------- ----------- ------------ --------- ----------- --------
18941684 1 2015-05-05 15:36:18 3 user 1000
I want to change streak to 0 if it has been longer then 12 days.
current query:
select
first_name, streak, user_id from myTable
where
club_id = 1000
and
post_streak = 1
and
streak_date between date_sub(now(),INTERVAL 12 DAY) and now()
order by streak desc;
Which doesn't show results older then 12 days. I want to show all results but change "streak" to 0 if it has been longer the 12 days.
What is the best way to go about this?
UPDATE table
SET (streak)
VALUES (0)
WHERE streak_date < DATEADD(DAY, -12, NOW() );
SELECT first_name, streak, user_id from myTable
WHERE
club_id = 1000
AND
post_streak = 1
ORDER BY streak DESC;
First query will set all streak values to 0 for records that have streak_date of more than 12 days ago
Second query will get a list of all your records that have a club_id of 1000 and a post_streak of 1
Put the condition in the select, rather than the where:
select first_name,
(case when streak_date between date_sub(now(), INTERVAL 12 DAY) and now()
then streak
else 0
end) as streak,
user_id from myTable
where club_id = 1000
order by streak desc;
I'm not sure if the post_streak condition is needed in the where clause.
http://sqlfiddle.com/#!9/d8bbd/6
select
user_id,
first_name,
streak_date,
IF(streak_date between date_sub(now(),INTERVAL 12 DAY) and now(),streak,0)
from myTable
where
club_id = 1000
and
post_streak = 1
order by streak desc;

Calculate index position using sub queries on group by using date

select user_id as sponsor_id,sum(points),created_at
from points_history
where created_at between '2014/08/12' and '2015/08/12' and transaction_type="debit"
group by user_id,DATE_FORMAT(created_at,"%d %M %Y")
order by DATE_FORMAT(created_at,"%d %M %Y"),sum(points) desc
sponsor_id sum(points) created_at
1 30 2014-12-08 10:54:59
2 25 2014-12-09 05:43:11
3 20 2014-12-09 06:58:40
1 5 2014-12-09 05:56:12
1 34 2014-08-23 10:42:32
here I want to calculate rank of particular sponsor using sponsor_id on daily basis .. I want to build a query that can return me something like as displayed below:
sponsor_id rank created_at
1 1 2014-12-08 10:54:59
1 3 2014-12-09 05:56:12
1 1 2014-08-23 10:42:32
I think I can use sub query like
select *
from (select user_id as sponsor_id,sum(points),created_at
from points_history
where created_at between '2014/08/12' and '2015/08/12' and transaction_type="debit"
group by user_id,DATE_FORMAT(created_at,"%d %M %Y")
order by DATE_FORMAT(created_at,"%d %M %Y"),sum(points) desc
) as t
where t.sponsor_id = 1
but how to calulate rank here.
Try this:
SELECT sponsor_id, points, created_at,
IF(#dte=#dte:=DATE(created_at), #rank:=#rank+1, #rank:=1) AS rank
FROM (SELECT user_id AS sponsor_id, SUM(points) points, created_at
FROM points_history
WHERE created_at BETWEEN '2014-08-12' AND '2015-08-12' AND
transaction_type = "debit"
GROUP BY user_id, DATE_FORMAT(created_at,"%d %M %Y")
ORDER BY DATE(created_at), SUM(points) DESC
) AS A, (SELECT #rank:=0, #dte:='') AS B
ORDER BY DATE(created_at), points DESC;