Count number of rows of a column SQL - mysql

How to make the count of rows of a specific column in a table:
ReportID Reader ReadTime
100 A 12:00
100 A 12:10
100 A 12:15
200 B 15:00
200 B 15:00
200 B 15:05
Expected OutCome:
ReportID Reader ReadTime Count Read by Reader and Time
100 A 12:00 1
100 A 12:10 1
100 A 12:15 1
200 B 15:00 2
200 B 15:00 2
200 B 15:05 1

You want to count without group by, this is done via over (so-called window functions)
COUNT(*) OVER (PARTITION BY ReportID, Reader, ReadTime)
Whether this works in your DB or not, I cannot tell (because you didn't tag).
However, here are some slides that explain window functions and also show which DBs support them.
https://www.slideshare.net/MarkusWinand/modern-sql/75

If your dbms doesn't support window functions, a simple correlated sub-query will do the trick:
select t1.ReportID, t1.Reader, t1.ReadTime,
(select count(*) from tablename t2
where t2.ReportID = t1.ReportID
and t2.Reader = t1.Reader
and t2.ReadTime = t1.ReadTime) as cnt
from tablename t1
Or, join with a derived table:
select t1.ReportID, t1.Reader, t1.ReadTime, t2.cnt
from tablename t1
join (select ReportID, Reader, ReadTime, count(*) as cnt
from tablename
group by ReportID, Reader, ReadTime) t2
on t2.ReportID = t1.ReportID
and t2.Reader = t1.Reader
and t2.ReadTime = t1.ReadTime

You could use :
Select reportid,reader,readtime,count(*) over (partition by reportid,reader,readtime) from table;

Simple do count(*) over()...
SELECT *, COUNT(*) OVER (PARTITION BY Reader, ReadTime) [Count] FROM <table>

Try
Select reportid,reader,readtime,count(*) from table1
group by Reader,readtime
http://sqlfiddle.com/#!9/2b938d/12

Related

Interpolate Multiseries Data In SQL

I have a system that stores the data only when they are changed. So, the dataset looks like below.
data_type_id
data_value
inserted_at
2
240
2022-01-19 17:20:52
1
30
2022-01-19 17:20:47
2
239
2022-01-19 17:20:42
1
29
2022-01-19 17:20:42
My data frequency is every 5 seconds. So, whether there's any timestamp or not I need to get the result by assuming in this 5th-second data value the same as the previous value.
As I am storing the data that are only changed, indeed the dataset should be like below.
data_type_id
data_value
inserted_at
2
240
2022-01-19 17:20:52
1
30
2022-01-19 17:20:52
2
239
2022-01-19 17:20:47
1
30
2022-01-19 17:20:47
2
239
2022-01-19 17:20:42
1
29
2022-01-19 17:20:42
I don't want to insert into my table, I just want to retrieve the data like this on the SELECT statement.
Is there any way I can create this query?
PS. I have many data_types hence when the OP makes a query, it usually gets around a million rows.
EDIT:
Information about server Server version: 10.3.27-MariaDB-0+deb10u1 Debian 10
The User is going to determine the SELECT DateTime. So, there's no certain between time.
As #Akina mentioned, sometimes there're some gaps between the inserted_at. The difference might be ~4seconds or ~6seconds instead of a certain 5seconds. Since it's not going to happen so frequently, It is okay to generate by ignoring this fact.
With the help of a query that gets you all the combinations of data_type_id and the 5-second moments you need, you can achieve the result you need using a subquery that gets you the closest data_value:
with recursive u as
(select '2022-01-19 17:20:42' as d
union all
select DATE_ADD(d, interval 5 second) from u
where d < '2022-01-19 17:20:52'),
v as
(select * from u cross join (select distinct data_type_id from table_name) t)
select v.data_type_id,
(select data_value from table_name where inserted_at <= d and data_type_id = v.data_type_id
order by inserted_at desc limit 1) as data_value,
d as inserted_at
from v
Fiddle
You can replace the recursive CTE with any query that gets you all the 5-second moments you need.
WITH RECURSIVE
cte1 AS ( SELECT #start_datetime dt
UNION ALL
SELECT dt + INTERVAL 5 SECOND FROM cte1 WHERE dt < #end_datetime),
cte2 AS ( SELECT *,
ROW_NUMBER() OVER (PARTITION BY test.data_type_id, cte1.dt
ORDER BY test.inserted_at DESC) rn
FROM cte1
LEFT JOIN test ON FIND_IN_SET(test.data_type_id, #data_type_ids)
AND cte1.dt >= test.inserted_at )
SELECT *
FROM cte2
WHERE rn = 1
https://dbfiddle.uk/?rdbms=mariadb_10.3&fiddle=380ad334de0c980a0ddf1b49bb6fa38e

Update columns based on calculation

My table looks like this:
id entry_date
1 21/12/2020 15:00
1 21/12/2020 17:00
1 21/12/2020 19:00
2 24/12/2020 00:00
2 24/12/2020 12:00
I have a list of id's connected to datestamps. I can manage to calculate the difference between their latest and first entry as follows:
SELECT id, TIMESTAMPDIFF(hour, MIN(entry_date), MAX(entry_date))
FROM mytable
GROUP BY id;
However, I am unsure how I can update my table to reflect these calculations. What I want is the following:
id entry_date time_difference
1 21/12/2020 15:00 4
1 21/12/2020 17:00 4
1 21/12/2020 19:00 4
2 24/12/2020 00:00 12
2 24/12/2020 12:00 12
In MySQL, you can self-join:
update mytable t
inner join (
select id,
timestampdiff(hour, min(entry_date), max(entry_date)) as time_difference
from mytable
group by id
) t1 on t1.id = t.id
set t.time_difference = t1.time_difference
I would not necessarily recommend storing this derived information, because it is hard to keep it up to date. Instead, you can create a view. If you are running MySQL 8.0:
create view myview as
select t.*,
timestampdiff(
hour,
min(entry_date) over(partition by id),
max(entry_date) over(partition by id)
) as timedifference
from mytable t
You can use a join in the update:
update mytable t join
(SELECT id, TIMESTAMPDIFF(hour, MIN(entry_date), MAX(entry_date)) as diff
FROM mytable
GROUP BY id
) tt
using (id)
set t.time_difference = tt.diff;

SQL How to select the value of the end of season(every three month)

Supposed I have some data as below:
code vol val num test_date
------------------------------------------
1 00001 500 0.1 111 20180105
2 00001 1000 0.2 222 20180304
3 00001 200 0.1 111 20180330
4 00001 400 0.3 222 20180601
5 00001 200 0.2 333 20180630
My expected result is
code vol val num test_date
------------------------------------------
1 00001 200 0.1 111 20180330
2 00001 200 0.2 333 20180630
3 00001 200 0.2 333 20180928 -- Max(val) only 0928, there is no data in 20180930
4 00001 200 0.2 333 20181231
I would like to select the max(val) for the month in '3, 6, 9 12', how to query in MySQL, thanks so much for any advice.
Since your dates are in numeric YYYYMMDD form, you can convert them to a "season" by integer dividing the date by 300. You can then find the maximum test_date per season and JOIN that back to the original table to get the values for that date:
SELECT d.*
FROM data d
JOIN (SELECT test_date DIV 300 AS quarter, MAX(test_date) AS max_date
FROM data
GROUP BY quarter) m ON m.max_date = d.test_date
Demo on dbfiddle
You can use quarter function available in mysql.
select * from test t1
inner join (
select quarter(test_date) qtr, max(val) val from test
group by quarter(test_date)) t2 on t2.val = t1.val and t2.qtr = quarter(t1.test_date)
see dbfiddle.
Hmmm . . . If you want the maximum value for each quarter, you can use window functions:
select t.*
from (select t.*,
row_number() over (partition by year(test_date), quarter(test_date) order by val desc) as seqnum
from t
) t
where seqnum = 1;
If you want the value on the last day of the quarter that is in the data, then use order by test_date desc instead:
select t.*
from (select t.*,
row_number() over (partition by year(test_date), quarter(test_date) order by test_date desc) as seqnum
from t
) t
where seqnum = 1;

Get last updated value SQL

I have the following table structure..
emp_id | base_rate | base_sal | effective_on
1001 26.22 1200 2015-10-12
1001 26.00 1100 2015-11-12
1001 26.00 1100 2015-12-12
1002 18 1200 2015-10-12
1002 19 1100 2015-11-12
I need to find get the last updated base_rate with effective_on date for each emp_id
Like output ..
1001 26.00 1100 2015-11-12
1002 19 1100 2015-11-12
See, for 1001 2015-11-12 is selected instead of 2015-12-12 which is latest as the base_rate is same and hence previously effective from 2015-11-12
I have tried.. everything.. not able to find the exact query..
This method is simple and easy to understand.
1) Assign rank for all the effective dates in descending order by partitioning
for each employee.
2) Select all the required fields for the last updated effective date from the
inner query and display the result.
SELECT emp_id,base_rate,base_sal
FROM
(
SELECT *,
ROW_NUMBER() OVER ( PARTITION BY emp_id ORDER BY effective_on DESC ) AS rn
FROM table
)
WHERE rn = 1;
One method is to generate a subset of employees with max effective on and join back to the base set..
In the below we generate set "B" with Emp_ID and ME (max effective) and then we join back to the entire data set in the table and use the columns emp_ID and ME to limit the data in the base set and return all columns we care about.
Put in English:
We generated a data set for all the employess with only their max effective date, and then joined this data set back to the base set to limit the data in the base set to only contain records for employees with their most recent effective_on date.
SELECT A.Emp_ID, A.Base_Rate, A.Base_Sal, min(C.Effective_On)
FROM Table A
INNER JOIN (SELECT emp_ID, Max(Effective_on) ME
FROM Table A
GROUP BY Emp_ID) B
on A.Emp_ID = B.Emp_ID
and A.Effective_ON = B.ME
INNER JOIN TABLE C
on C.Emp_ID = A.Emp_ID
and C.Base_Rate= A.Base_rate
and C.base_Sal = A.Base_Sal
GROUP BY A.Emp_ID, A.Base_Rate, A.Base_Sal
This is more or less database agnostic whereas a row_number and limit would not work on mySQL as it doesn't support window functions.
You can first get the minimum date each base_rate becomes effective on for every employee and then take the max from there. Here is how you can do it using row_number() in oracle:
with temp(emp_id, base_rate, base_sal, effective_on)
as (select 1001, 26.22, 1200, '2015-10-12' from dual union all
select 1001, 26.00, 1100, '2015-11-12' from dual union all
select 1001, 26.00, 1100, '2015-12-12' from dual union all
select 1002, 18, 1200, '2015-10-12' from dual union all
select 1002, 19, 1100, '2015-11-12' from dual
)
SELECT emp_id,base_rate,base_sal,effective_on FROM(
SELECT temp2.*,
row_number() OVER (PARTITION BY EMP_ID ORDER BY effective_on DESC) AS rn2
FROM
(
SELECT temp.*,
row_number() OVER (PARTITION BY EMP_ID, BASE_RATE ORDER BY effective_on) AS rn
FROM temp
) temp2
WHERE rn = 1
)
WHERE rn2 = 1;

select price having max year in another column

I have following select result
Code Price Year
1 200 2013
1 100 2012
2 250 2011
2 275 2012
2 300 2010
But I want following something like this with one extra column which hold price based on maximum year,
Code Price Year ExPrice
1 200 2013 200
1 100 2012 200
2 250 2011 275
2 275 2012 275
2 300 2010 275
Sorry for bad English and wrong way for asking this question.
You can do it with cross apply and select top 1 ... order by:
select Code, Price, Year, ExPrice
from TableName T
cross apply (
select top 1 Price
from TableName
where Code = T.Code
order by Year desc
) p(ExPrice)
or row_number and join (whatever you prefer):
;with cte as (
select Code, Price as ExPrice, rn = row_number() over (partition by Code order by Year desc)
from TableName
)
select T.Code, Price, Year, ExPrice
from TableName T
join cte on cte.Code = T.Code and cte.rn = 1
SQLFiddle sample
Try something like this:
SELECT T1.Code, T1.Price, T1.Year, T2.Price
FROM Table T1
INNER JOIN Table T2 ON T1.Code = T2.Code AND
T2.Year = (SELECT MAX(Year) FROM Table WHERE Table.Code = T2.Code)