Getting max value in a specific peroid of time in MySQL - mysql

I have a table like this(tblFuel):
time fuel
2014-11-04 17:11:08 231
2014-11-04 17:34:16 254
2014-11-04 18:03:48 241
2014-11-04 18:41:34 137
2014-11-04 18:43:42 111
Now I expect to show the biggest value of fuel during each 1 hour. For example: max from 17:00:00 to 17:59:59 and so on. And follow the previous requirement, the expected result should:
time fuel
2014-11-04 17:34:16 254
2014-11-04 18:03:48 241
So what should I do to achieve this result?

create table tblFuel (time timestamp, fuel int);
insert into tblFuel values ('2014-11-04 17:11:08', 231);
insert into tblFuel values ('2014-11-04 17:34:16', 254);
insert into tblFuel values ('2014-11-04 18:03:48', 241);
insert into tblFuel values ('2014-11-04 18:41:34', 137);
insert into tblFuel values ('2014-11-04 18:43:42', 111);
select
*
from tblFuel
where concat(date(time), hour(time), fuel) in
(select
concat(date(time), hour(time), max(fuel))
from tblFuel
group by
date(time),
hour(time))
Returns:
time fuel
2014-11-04 17:34:16 254
2014-11-04 18:03:48 241

This query will be helpful.
DECLARE #tblFuel TABLE
(
Val INTEGER,
Time DATETIME
)
INSERT INTO #tblFuel
SELECT '231', '2014-11-04 17:11:08' union All
SELECT '254', '2014-11-04 17:34:16' union All
SELECT '241', '2014-11-04 18:03:48' union All
SELECT '137', '2014-11-04 18:41:34' union All
SELECT '111', '2014-11-04 18:43:42'
SELECT A.Val, A.Time FROM #tblFuel AS A
Inner join
(SELECT MAX(Val) AS VAL,
CONVERT(VARCHAR(20), Time, 110) +' ' + CAST(DATEPART(hour, Time) as varchar(2)) AS Time
FROM #tblFuel GROUP BY CONVERT(VARCHAR(20), Time, 110) +' ' + CAST(DATEPART(hour, Time) as varchar(2))) AS B
ON A.Val = B.val AND CONVERT(VARCHAR(20), A.Time, 110) +' ' + CAST(DATEPART(hour, A.Time) as varchar(2)) = B.Time

Here's one way to do it. It uses the DATE_FORMAT function to group by date and hour.
SELECT ft.time, ft.fuel
FROM fuel_table ft
JOIN
(SELECT DATE_FORMAT(time, '%Y%m%d %H') date_and_hour, MAX(fuel) max_fuel
FROM fuel_table
GROUP BY date_and_hour) AS max_fuel
ON DATE_FORMAT(ft.time, '%Y%m%d %H') = max_fuel.date_and_hour
AND ft.fuel = max_fuel

Related

SQL group by school name

school_name
class
medium
total
srk
1
english
13
srk
2
english
14
srk
3
english
15
srk
1
french
16
srk
2
french
16
srk
3
french
18
vrk
1
english
17
vrk
1
french
18
I want that output by
school_name
class1eng
class1french
class2eng
class2french
class3english
class3french
[output needed][ otput required
output
You’re looking for multiple select statements along with appropriate cases to satisfy.
This should work for you
Select
school_name,
Sum(Case when (class=1 and medium=‘English’) then total else 0 end) as class1english,
Sum(Case when (class=1 and medium=‘French’) then total else 0 end) as class1french,
Sum(Case when (class=2 and medium=‘English’) then total else 0 end) as class2english,
Sum(Case when (class=2 and medium=‘French’) then total else 0 end) as class2french,
Sum(Case when (class=3 and medium=‘English’) then total else 0 end) as class3english,
Sum(Case when (class=3 and medium=‘French’) then total else 0 end) as class3french
From
table_name
Group by
school_name
Seems to be a simple ask, assumed you also want to order your results. Please check below query if that helps
SELECT school_name, class, medium, SUM(total) AS Total
FROM <Table Name>
GROUP BY school_name, class, medium
This solution is for general purpose, complex, but functional.
I've made it for myself as exercise and challenge.
/* --------------- TABLE --------------- */
CREATE TABLE schools_tab
(school VARCHAR(9), class INT, subj VARCHAR(9), total INT);
INSERT INTO schools_tab VALUES
('srk', 1, 'english', 13),
('srk', 2, 'english', 14),
('srk', 3, 'english', 15),
('srk', 1, 'french', 16),
('srk', 2, 'french', 16),
('srk', 3, 'french', 18),
('vrk', 1, 'english', 17),
('vrk', 1, 'french', 18);
/* -------------- DYNAMIC QUERY --------------- */
SET #sql=NULL;
WITH cte AS (
SELECT school, class, subj, ROW_NUMBER() OVER (PARTITION BY school) AS idx, DENSE_RANK() OVER (ORDER BY school) AS ids
FROM (SELECT DISTINCT school FROM schools_tab) A LEFT JOIN (SELECT DISTINCT class, subj FROM schools_tab) B ON (1=1)
), cte2 AS (
SELECT A.ids, A.idx, A.school, A.class, A.subj, COALESCE(B.total, 0) AS total
FROM cte A LEFT JOIN schools_tab B ON (A.school=B.school AND A.class=B.class AND A.subj=B.subj)
), cte3 AS (
SELECT DISTINCT class, subj
FROM schools_tab
ORDER BY class, subject
)
SELECT CONCAT('WITH RECURSIVE cte AS (
SELECT school, class, subj, ROW_NUMBER() OVER (PARTITION BY school) AS idx, DENSE_RANK() OVER (ORDER BY school) AS ids
FROM (SELECT DISTINCT school FROM schools_tab) A LEFT JOIN (SELECT DISTINCT class, subj FROM schools_tab) B ON (1=1)
), cte2 AS (
SELECT A.ids, A.idx, A.school, A.class, A.subj, COALESCE(B.total, 0) AS total
FROM cte A LEFT JOIN schools_tab B ON (A.school=B.school AND A.class=B.class AND A.subj=B.subj)
), ctx AS ('
'SELECT (SELECT MAX(ids) FROM cte2) AS n,',
GROUP_CONCAT(DISTINCT CONCAT( '(SELECT total FROM cte2 WHERE idx=',idx,' AND ids=n) AS class',class,subj ) ORDER BY class, subj),
' UNION ALL SELECT n-1 AS n,',
GROUP_CONCAT(DISTINCT CONCAT( '(SELECT total FROM cte2 WHERE idx=',idx,' AND ids=n) AS class',class,subj ) ORDER BY class, subj),
' FROM ctx WHERE n>0',
') SELECT DISTINCT SUBSTRING_INDEX(SUBSTRING_INDEX(''srk,vrk'', '','', n+1), '','', -1) AS school,',
GROUP_CONCAT(DISTINCT CONCAT('class',class,subj)),
' FROM ctx ORDER BY school'
) INTO #sql
FROM cte2;
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;

I want to query the no of transaction done by a customer in a particular year, but the output should come year wise for each customer in table format

Output should be in below format, but I am getting wrong output:
Where 2019,2020,2021 column contains transaction done by customer in respectively 2019, 2020, 2021. Also if transactions in 2019,2020,2021 is equal Max_transaction is populated with first non-zero transaction year .
customer_name 2019 2020 2021 Max_transaction_year total_transaction
pug 2 1 0 2019 4
hari 0 1 1 2020 2
adh 0 0 1 2021 1
Sample table and data :
Also note that the first two digits in "tid" represent the year of transaction. Eg: 19597 -'19' represents 2019 and so on for 2020 and 2021.
create table client (cid int,cname char(10));
create table trans (tid int,cid int);
insert into client values(102,'pug'),(107,'ravi'),(109,'hari'),(105,'pon'),(106,'adh'),(104,'bav'),(101,'kat');
insert into trans values(19597,102),(19567,102),(20325,109),(21789,106),(17432,106),(21786,109),(20302,102),(17301,103);
Thanks in advance
Schema (MySQL v8.0)
create table client (cid int,cname char(10));
create table trans (tid int,cid int);
insert into client values(102,'pug'),(107,'ravi'),(109,'hari'),(105,'pon'),(106,'adh'),(104,'bav'),(101,'kat');
insert into trans values(19597,102),(19567,102),(20325,109),(21789,106),(17432,106),(21786,109),(20302,102),(17301,103);
Query #1
SELECT
customer_name,
SUM(
CASE WHEN year=2019 THEN no_transactions ELSE 0 END
) as '2019',
SUM(
CASE WHEN year=2020 THEN no_transactions ELSE 0 END
) as '2020',
SUM(
CASE WHEN year=2021 THEN no_transactions ELSE 0 END
) as '2021',
MAX(
CASE WHEN rn=1 THEN year ELSE 0 END
) as Max_transaction_year,
SUM(no_transactions) as total_transaction
FROM (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY customer_name ORDER BY no_transactions DESC) rn
FROM (
SELECT
c.cname as customer_name,
2000+FLOOR(tid / 1000) as year ,
COUNT(1) as no_transactions
FROM
trans t
INNER JOIN
client c ON t.cid = c.cid
WHERE
FLOOR(tid / 1000) BETWEEN 19 and 21
GROUP BY
c.cname, 2000+FLOOR(tid / 1000)
) p1
) p2
GROUP BY customer_name;
customer_name
2019
2020
2021
Max_transaction_year
total_transaction
adh
0
0
1
2021
1
hari
0
1
1
2020
2
pug
2
1
0
2019
3
View on DB Fiddle
I think a somewhat simpler method just uses conditional aggregation:
select cname, cnt_2019, cnt_2020, cnt_2021,
(case greatest(cnt_2019, cnt_2020, cnt_2021)
when cnt_2019 then '2019'
when cnt_2020 then '2020'
when cnt_2021 then '2021'
end) as max_year,
total_transactions
from (select c.cname, c.cid,
sum(year = '2019') as cnt_2019,
sum(year = '2020') as cnt_2020,
sum(year = '2021') as cnt_2021,
count(*) as total_transactions
from client c join
(select t.*, concat('20', left(t.tid, 2)) as year
from trans t
) t
on c.cid = t.cid
where year >= '2019' and year <= '2021'
group by c.cname, c.cid
) ct
order by cname, cid;
Here is a db<>fiddle.

How To Write A Query With A CTE And A Left Join

I am trying to have a calendar table with my CTE and set it up so that my dates in my query display like this
Jan 18Jan 19Feb 18Feb 19
Now this is my DDL and this is the query I attempted, but in MySql Workbench I'm getting the error that there is an error in my sql somewhere.
This is the exact error:
Query Error: Error: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'RECURSIVE cte_months_to_pull AS ( SELECT DATE_FORMAT(#start_date, '%Y-%m-01'' at line 1
Can someone assist?
If it's easier this is a SQL Fiddle of everything http://sqlfiddle.com/#!9/300f9d/1
CREATE TABLE PrevYear (
`EmployeeNumber` char(8) NOT NULL,
`SaleAmount` int DEFAULT NULL,
`SaleDate` date NOT NULL,
`EmployeeName` char(17) NOT NULL
);
CREATE TABLE CurrentYear (
`EmployeeNumber` char(8) NOT NULL,
`SaleAmount` int DEFAULT NULL,
`SaleDate` date NOT NULL,
`EmployeeName` char(17) NOT NULL
);
INSERT INTO CurrentYear
VALUES ('ea12', '100', '2019-01-10', 'Maggie Samuels');
INSERT INTO CurrentYear
VALUES ('ea12', '199', '2019-01-13', 'Sam Stoner');
INSERT INTO CurrentYear
VALUES ('ea12', '100', '2019-03-01', 'Jake Jolel');
INSERT INTO CurrentYear
VALUES ('ls22', '100', '2019-05-01', 'Maggie Samuels');
INSERT INTO PrevYear
VALUES ('ea12', '100', '2018-01-10', 'Maggie Samuels');
INSERT INTO PrevYear
VALUES ('ea12', '199', '2018-01-13', 'Sam Stoner');
INSERT INTO PrevYear
VALUES ('ea12', '100', '2018-03-01', 'Sam Stoner');
INSERT INTO PrevYear
VALUES ('ls22', '100', '2018-05-01', 'Maggie Samuels');
And this is the query I try:
SET #start_date = '20190102';
SET #number_of_months = 12;
WITH RECURSIVE
cte_months_to_pull AS (
SELECT DATE_FORMAT(#start_date, '%Y-%m-01')
- INTERVAL #number_of_months MONTH AS month_to_pull
UNION ALL
SELECT month_to_pull + INTERVAL 1 MONTH
FROM cte_months_to_pull
WHERE month_to_pull < #start_date + INTERVAL #number_of_months - 2 MONTH
)
SELECT Date_format(saledate, '%m-%Y') AS Month,
employeename,
Sum(saleamount) AS IA
FROM currentyear
WHERE employeename = 'Maggie Samuels'
GROUP BY Date_format(saledate, '%m-%Y'), employeename
UNION ALL
SELECT Date_format(saledate, '%m-%Y') AS Month,
employeename,
Sum(saleamount) AS IA
FROM prevyear
WHERE employeename = 'Maggie Samuels'
GROUP BY Date_format(saledate, '%m-%Y'), employeename
LEFT JOIN cte_months_to_pull (
Select DATE_Format(month_to_pull, '%b %y')
FROM cte_months_to_pull
) AS YRS ON month_to_pull = saledate
ORDER BY MONTH(month_to_pull), YEAR(month_to_pull)
As I can see you are using MySQL version older than 8.0 which doesn't support RECURSIVE CTEs. I have tried your query with some minor updates on 8.0 and it worked fine -
WITH RECURSIVE
cte_months_to_pull AS (
SELECT DATE_FORMAT(#start_date, '%Y-%m-01')
- INTERVAL #number_of_months MONTH AS month_to_pull
UNION ALL
SELECT month_to_pull + INTERVAL 1 MONTH
FROM cte_months_to_pull
WHERE month_to_pull < #start_date + INTERVAL #number_of_months - 2 MONTH
)
SELECT YRS.months_to_pull
,T.employeename
,COALESCE(T.IA, 0) IA
FROM (SELECT DATE_Format(month_to_pull, '%b-%Y') months_to_pull
FROM cte_months_to_pull
ORDER BY months_to_pull
) AS YRS
LEFT JOIN (SELECT Date_format(saledate, '%b-%Y') AS `Month`
,employeename
,Sum(saleamount) AS IA
FROM CurrentYear
WHERE employeename = 'Maggie Samuels'
GROUP BY Date_format(saledate, '%b-%Y'), employeename
UNION ALL
SELECT Date_format(saledate, '%b-%Y')
,employeename
,Sum(saleamount)
FROM PrevYear
WHERE employeename = 'Maggie Samuels'
GROUP BY Date_format(saledate, '%b-%Y'), employeename) T
ON YRS.months_to_pull = T.`Month`
ORDER BY month(STR_TO_DATE(CONCAT('01-',months_to_pull), '%d-%b-%Y'))
,YEAR(STR_TO_DATE(CONCAT('01-',months_to_pull), '%d-%b-%Y'))
Here is the Fiddle
Since there is no expected output, I have only tried till running the query.

sql server 2008 running totals between 2 dates

I need to get running totals between 2 dates in my sql server table and update the records simultaneoulsy. My data is as below and ordered by date,voucher_no
DATE VOUCHER_NO OPEN_BAL DEBITS CREDITS CLOS_BAL
-------------------------------------------------------------------
10/10/2017 1 100 10 110
12/10/2017 2 110 5 105
13/10/2017 3 105 20 125
Now if i insert a record with voucher_no 4 on 12/10/2017 the output should be like
DATE VOUCHER_NO OPEN_BAL DEBITS CREDITS CLOS_BAL
------------------------------------------------------------------
10/10/2017 1 100 10 110
12/10/2017 2 110 5 105
12/10/2017 4 105 4 109
13/10/2017 3 109 20 129
I have seen several examples which find running totals upto a certain date but not between 2 dates or from a particular date to end of file
You should consider changing your database structure. I think it will be better to keep DATE, VOUCHER_NO, DEBITS, CREDITS in one table. And create view to calculate balances. In that case you will not have to update table after each insert. In this case your table will look like
create table myTable (
DATE date
, VOUCHER_NO int
, DEBITS int
, CREDITS int
)
insert into myTable values
('20171010', 1, 10, null),( '20171012', 2, null, 5)
, ('20171013', 3, 20, null), ('20171012', 4, 4, null)
And view will be
;with cte as (
select
DATE, VOUCHER_NO, DEBITS, CREDITS, bal = isnull(DEBITS, CREDITS) * case when DEBITS is null then -1 else 1 end
, rn = row_number() over (order by DATE, VOUCHER_NO)
from
myTable
)
select
a.DATE, a.VOUCHER_NO, a.DEBITS, a.CREDITS
, OPEN_BAL = sum(b.bal + case when b.rn = 1 then 100 else 0 end) - a.bal
, CLOS_BAL = sum(b.bal + case when b.rn = 1 then 100 else 0 end)
from
cte a
join cte b on a.rn >= b.rn
group by a.DATE, a.VOUCHER_NO, a.rn, a.bal, a.DEBITS, a.CREDITS
Here's another solution if you can not change your db structure. In this case you must run update statement each time after inserts. In both cases I assume that initial balance is 100 while recalculation
create table myTable (
DATE date
, VOUCHER_NO int
, OPEN_BAL int
, DEBITS int
, CREDITS int
, CLOS_BAL int
)
insert into myTable values
('20171010', 1, 100, 10, null, 110)
,( '20171012', 2, 110, null, 5, 105)
, ('20171013', 3, 105, 20, null, 125)
, ('20171012', 4, null, 4, null, null)
;with cte as (
select
DATE, VOUCHER_NO, DEBITS, CREDITS, bal = isnull(DEBITS, CREDITS) * case when DEBITS is null then -1 else 1 end
, rn = row_number() over (order by DATE, VOUCHER_NO)
from
myTable
)
, cte2 as (
select
a.DATE, a.VOUCHER_NO
, OPEN_BAL = sum(b.bal + case when b.rn = 1 then 100 else 0 end) - a.bal
, CLOS_BAL = sum(b.bal + case when b.rn = 1 then 100 else 0 end)
from
cte a
join cte b on a.rn >= b.rn
group by a.DATE, a.VOUCHER_NO, a.rn, a.bal
)
update a
set a.OPEN_BAL = b.OPEN_BAL, a.CLOS_BAL = b.CLOS_BAL
from
myTable a
join cte2 b on a.DATE = b.DATE and a.VOUCHER_NO = b.VOUCHER_NO

altering a table using sql

I have this table
**Original Table**
year month duration amount per month
2012 5 3 2000
and I want to get this
**Result table**
year month duration amount per month
2012 5 1 2000
2012 6 1 2000
2012 7 1 2000
Note how the duration of a project (this is a project) is 3 and the "amount per month" is 2000, so I added two more rows to show that the next months (6 and 7) will have an "amount per month" as well. How do I do that with sql/tsql?
try this for SQL SERVER, i included my test temp table:
declare #temp as table
(
[year] int
, [month] int
, [duration] int
, [amount] int
)
insert into #temp
(
[year]
, [month]
, [duration]
, [amount]
)
VALUES(
2012
,5
,3
,2000
)
SELECT
[year]
,[month] + n.number
,1
,[amount]
, '1' + SUBSTRING(CAST([duration] AS varchar(10)), 2, 1000) AS Items
FROM #temp
JOIN master..spt_values n
ON n.type = 'P'
AND n.number < CONVERT(int, [duration])
Please see the script below that may work for your requirement. I have also compensated for calender year and month increment. Please test and let me know.
DECLARE #temp AS TABLE([Year] INT,[Month] INT,Duration INT,Amount INT)
INSERT INTO #temp([year], [month], Duration, Amount)
VALUES (2011, 5, 3, 2000),(2012, 11, 3, 3000),(2013, 9, 12, 1000);
;WITH cte_datefix
AS (
SELECT [Year],
[Month],
Duration,
Amount,
CAST(CAST([Year] AS VARCHAR(4)) + RIGHT('00' + CAST([Month] AS VARCHAR(2)), 2) + '01' AS DATE) AS [Date]
FROM #temp
),
cte_Reslut
AS (SELECT [Year],
[Month],
Duration,
Amount,
[Date],
1 AS Months
FROM cte_datefix
UNION ALL
SELECT t.[Year],
t.[Month],
t.Duration,
t.Amount,
DATEADD(M, Months, t.[Date]) AS [Date],
cr.Months + 1 AS Months
FROM cte_Reslut AS cr
INNER JOIN cte_datefix AS t
ON t.[Year] = cr.[Year]
WHERE cr.Months < cr.Duration
)
SELECT YEAR([Date]) AS [Year],
MONTH([Date]) AS [Month],
1 AS Duration,
Amount
FROM cte_Reslut
ORDER BY [Date]
For those that are wondering how to increment the year if needed, here is an example building on Suing response (really easy, just include two case statements):
select
2012 as [year]
,11 as [month]
,5 as [duration]
,2000 as [amount]
into #temp
select * from #temp
SELECT
case
when [month] + n.number > 12
then [year] + 1
else [year]
end as [year]
,case
when [month] + n.number > 12
then [month] + n.number - 12
else [month] + n.number
end as newYear
,1 as newDuration
,[amount]
, '1' + SUBSTRING(CAST([duration] AS varchar(10)), 2, 1000) AS Items
FROM #temp
JOIN master..spt_values n
ON n.type = 'P'
AND n.number < CONVERT(int, [duration])
drop table #temp