Select Highest value against each record in SQL - sql

I am new to SQL and the problem I am having is that I have the value for alot assets in a table.
I need to get the highest speed for each asset in that table.
I have tried searching google but I found the MAX() function of SQL.
I don't need the MAX() because that will only give me one record with the highest value. I need the highest for each asset:
e.g.
iAssetId fSpeedKPH
1 78
5 77
5 80
8 74
8 81
8 88
8 111
24 71
24 78
24 79
24 79
24 82
24 84
24 90
24 91
24 92
I have highlighted the highest row for each asset i.e. AssetId = 1, 5, 24 and 8
These are the rows I need to select.
What is the most efficient way?
Do I have to loop through this result-set returned by the SQL I have written?
EDIT:
My SQL:
DECLARE #dateMinusDay datetime = DateAdd(dd, -1, GetDate())
select vm.iAssetId, max(vm.fSpeedKPH), vm.dtUTCDateTime, ge.sGeofenceName from VehicleMonitoringLog vm
inner join Geofences ge on ge.iGeofenceId = vm.iGeofenceId
where vm.iGeofenceId != 1 AND vm.fSpeedKPH > 70 AND (vm.dtUTCDateTime > #dateMinusDay AND vm.dtUTCDateTime < GETDATE())
group by
vm.iAssetId,vm.fSpeedKPH, vm.dtUTCDateTime, ge.sGeofenceName

select iAssetId, max(fSpeedKPH)
from AssetsTable
group by iAssetId

SELECT iAssetId, fSpeedKPH
FROM (
SELECT iAssetId, fSpeedKPH
,ROW_NUMBER() OVER (PARTITION BY iAssetId ORDER BY fSpeedKPH DESC) AS RN
FROM Table_Name )Sub
WHERE RN = 1
UPDATE
DECLARE #dateMinusDay datetime = DateAdd(dd, -1, GetDate())
SELECT Q.iAssetId, Q.dtUTCDateTime, Q.sGeofenceName
FROM (
select vm.iAssetId
, vm.dtUTCDateTime
, ge.sGeofenceName
,ROW_NUMBER() OVER (PARTITION BY vm.iAssetId ORDER BY vm.fSpeedKPH DESC) AS RN
from VehicleMonitoringLog vm inner join Geofences ge
on ge.iGeofenceId = vm.iGeofenceId
where vm.iGeofenceId != 1 AND vm.fSpeedKPH > 70
AND (vm.dtUTCDateTime > #dateMinusDay --<-- Instead of variable you can use GETDATE() - 1
AND vm.dtUTCDateTime < GETDATE())
)Q
WHERE RN = 1

Related

Snowflake SQL - Count Distinct Users within descending time interval

I want to count the distinct amount of users over the last 60 days, and then, count the distinct amount of users over the last 59 days, and so on and so forth.
Ideally, the output would look like this (TARGET OUTPUT)
Day Distinct Users
60 200
59 200
58 188
57 185
56 180
[...] [...]
where 60 days is the max total possible distinct users, and then 59 would have a little less and so on and so forth.
my query looks like this.
select
count(distinct (case when datediff(day,DATE,current_date) <= 60 then USER_ID end)) as day_60,
count(distinct (case when datediff(day,DATE,current_date) <= 59 then USER_ID end)) as day_59,
count(distinct (case when datediff(day,DATE,current_date) <= 58 then USER_ID end)) as day_58
FROM Table
The issue with my query is that This outputs the data by column instead of by rows (like shown below) AND, most importantly, I have to write out this logic 60x for each of the 60 days.
Current Output:
Day_60 Day_59 Day_58
209 207 207
Is it possible to write the SQL in a way that creates the target as shown initially above?
Using below data in CTE format -
with data_cte(dates,userid) as
(select * from values
('2022-05-01'::date,'UID1'),
('2022-05-01'::date,'UID2'),
('2022-05-02'::date,'UID1'),
('2022-05-02'::date,'UID2'),
('2022-05-03'::date,'UID1'),
('2022-05-03'::date,'UID2'),
('2022-05-03'::date,'UID3'),
('2022-05-04'::date,'UID1'),
('2022-05-04'::date,'UID1'),
('2022-05-04'::date,'UID2'),
('2022-05-04'::date,'UID3'),
('2022-05-04'::date,'UID4'),
('2022-05-05'::date,'UID1'),
('2022-05-06'::date,'UID1'),
('2022-05-07'::date,'UID1'),
('2022-05-07'::date,'UID2'),
('2022-05-08'::date,'UID1')
)
Query to get all dates and count and distinct counts -
select dates,count(userid) cnt, count(distinct userid) cnt_d
from data_cte
group by dates;
DATES
CNT
CNT_D
2022-05-01
2
2
2022-05-02
2
2
2022-05-03
3
3
2022-05-04
5
4
2022-05-05
1
1
2022-05-06
1
1
2022-05-08
1
1
2022-05-07
2
2
Query to get difference of date from current date
select dates,datediff(day,dates,current_date()) ddiff,
count(userid) cnt,
count(distinct userid) cnt_d
from data_cte
group by dates;
DATES
DDIFF
CNT
CNT_D
2022-05-01
45
2
2
2022-05-02
44
2
2
2022-05-03
43
3
3
2022-05-04
42
5
4
2022-05-05
41
1
1
2022-05-06
40
1
1
2022-05-08
38
1
1
2022-05-07
39
2
2
Get records with date difference beyond a certain range only -
include clause having
select datediff(day,dates,current_date()) ddiff,
count(userid) cnt,
count(distinct userid) cnt_d
from data_cte
group by dates
having ddiff<=43;
DDIFF
CNT
CNT_D
43
3
3
42
5
4
41
1
1
39
2
2
38
1
1
40
1
1
If you need to prefix 'day' to each date diff count, you can
add and outer query to previously fetched data-set and add the needed prefix to the date diff column as following -
I am using CTE syntax, but you may use sub-query given you will select from table -
,cte_1 as (
select datediff(day,dates,current_date()) ddiff,
count(userid) cnt,
count(distinct userid) cnt_d
from data_cte
group by dates
having ddiff<=43)
select 'day_'||to_char(ddiff) days,
cnt,
cnt_d
from cte_1;
DAYS
CNT
CNT_D
day_43
3
3
day_42
5
4
day_41
1
1
day_39
2
2
day_38
1
1
day_40
1
1
Updated the answer to get distinct user count for number of days range.
A clause can be included in the final query to limit to number of days needed.
with data_cte(dates,userid) as
(select * from values
('2022-05-01'::date,'UID1'),
('2022-05-01'::date,'UID2'),
('2022-05-02'::date,'UID1'),
('2022-05-02'::date,'UID2'),
('2022-05-03'::date,'UID5'),
('2022-05-03'::date,'UID2'),
('2022-05-03'::date,'UID3'),
('2022-05-04'::date,'UID1'),
('2022-05-04'::date,'UID6'),
('2022-05-04'::date,'UID2'),
('2022-05-04'::date,'UID3'),
('2022-05-04'::date,'UID4'),
('2022-05-05'::date,'UID7'),
('2022-05-06'::date,'UID1'),
('2022-05-07'::date,'UID8'),
('2022-05-07'::date,'UID2'),
('2022-05-08'::date,'UID9')
),cte_1 as
(select datediff(day,dates,current_date()) ddiff,userid
from data_cte), cte_2 as
(select distinct ddiff from cte_1 )
select cte_2.ddiff,
(select count(distinct userid)
from cte_1 where cte_1.ddiff <= cte_2.ddiff) cnt
from cte_2
order by cte_2.ddiff desc
DDIFF
CNT
47
9
46
9
45
9
44
8
43
5
42
4
41
3
40
1
You can do unpivot after getting your current output.
sample one.
select
*
from (
select
209 Day_60,
207 Day_59,
207 Day_58
)unpivot ( cnt for days in (Day_60,Day_59,Day_58));

How to join 3 records as 1 record base on a same date?

This are some sample queries I wrote:
SELECT
CAST(datecolumn AS DATE) AS DateColumn,
COUNT(*) AS count
FROM
dbo.myTableName
WHERE
status = 'stage1'
GROUP BY CAST(datecolumn AS DATE) ORDER BY DateColumn DESC;
SELECT
CAST(datecolumn AS DATE) AS DateColumn,
COUNT(*) AS count
FROM
dbo.myTableName
WHERE
status = 'stage2'
GROUP BY CAST(datecolumn AS DATE) ORDER BY DateColumn DESC;
This is the output from the 1st query:
DateColumn count
------------------
2022-05-26 23
2022-05-25 51
2022-05-24 39
2022-05-23 55
2022-05-22 27
2022-05-21 90
and this is the output from the 2nd query:
DateColumn count
-----------------
2022-05-26 31
2022-05-25 67
2022-05-24 38
2022-05-23 54
2022-05-22 28
I want to only have a single query that will output it like this
DateColumn stage1count stage2count
-----------------------------------
2022-05-26 23 31
2022-05-25 51 67
2022-05-24 39 38
2022-05-23 55 54
2022-05-22 27 28
Thanks for answer
Can you try this:
select cast(datecolumn as DATE) as DateColumn,
sum(case when status = 'stage1' then 1 else 0 end) as stage1count,
sum(case when status = 'stage2' then 1 else 0 end) as stage2count
from dbo.myTableName
where status in ('stage1', 'stage2')
group by cast(datecolumn as DATE)
order by DateColumn DESC
Another note: Most SQL systems treat datecolumn and DateColumn the same, so it is somewhat ambiguous which it is actually using in the group by and order by clauses. I think the order by is using the casted value in the select list, and the groupby might be using the base column (uncasted) but I'm not sure about that. If you want to avoid the ambiguity, you can use a delimited identifier "DateColumn" instead.
#hewszz, you mention that you also need this for the case where you have two tables. This might do the job if you have two tables:
select t1.DateColumn, stage1count, stage2count
from (select cast(datecolumn as DATE) as DateColumn,
count(*) as stage1count
from dbo.myTableName1
where status = 'stage1'
group by cast(datecolumn as DATE)) t1
full outer join
(select cast(datecolumn as DATE) as DateColumn,
count(*) as stage2count
from dbo.myTableName2
where status = 'stage2'
group by cast(datecolumn as DATE)) t2
on t1.DateColumn = t2.DateColumn
order by t1.DateColumn DESC
By grouping each table separately we make sure that DateColumn is unique on each side, so each row will join with at most one row from the other grouped query. By using a full outer join we make sure no rows get lost when we have only a stage1 or a stage2 record for a given day.

SQLite query - Limit occurrence of value

I have a query that return this result. How can i limit the occurrence of a value from the 4th column.
19 1 _BOURC01 1
20 1 _BOURC01 3 2019-11-18
20 1 _BOURC01 3 2017-01-02
21 1 _BOURC01 6
22 1 _BOURC01 10
23 1 _BOURC01 13 2016-06-06
24 1 _BOURC01 21 2016-09-19
My Query:
SELECT "_44_SpeakerSpeech"."id" AS "id", "_44_SpeakerSpeech"."active" AS "active", "_44_SpeakerSpeech"."id_speaker" AS "id_speaker", "_44_SpeakerSpeech"."Speech" AS "Speech", "34 Program Weekend"."date" AS "date"
FROM "_44_SpeakerSpeech"
LEFT JOIN "_34_programWeekend" "34 Program Weekend" ON "_44_SpeakerSpeech"."Speech" = "34 Program Weekend"."theme_id"
WHERE "id_speaker" = "_BOURC01"
ORDER BY id_speaker, Speech, date DESC
Thanks
I think this is what you want here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY s.id, s.active, s.id_speaker, s.Speech
ORDER BY p.date DESC) rn
FROM "_44_SpeakerSpeech" s
LEFT JOIN "_34_programWeekend" p ON s.Speech = p.theme_id
WHERE s.id_speaker = '_BOURC01'
)
SELECT id, active, id_speaker, Speech, date
FROM cte
WHERE rn = 1;
This logic assumes that when two or more records all have the same columns values (excluding the date), you want to retain only the latest record.

writing a query for table and giving a score

I am trying to write a query for the following:
Count the number of transactions in the previous 90 days. Note: This needs to be averaged out for new members = (No of transactions / Days being a member) x 90
New members are the one whose DateCreated is between 0 and 90 days from todays date.
Table structure:
Column Name Datatype
---------------------------------
Member_No nvarchar(255)
Order_No int
Transaction_Date datetime
Net money
Date_Created datetime
Also, the final step is after counting the transactions, I need to give a score. So if a member has count more than 8 then give a score of 5.
Following are the ranges:`
Transaction count Score
>8 5
6-8 4
4-6 3
2-4 2
0-2 1
Sample Data:
Member No Sales Order Number Date Created Transaction Date Net
M1 2332 01-10-15 10-07-16 354
M2 2311 12-12-16 14-12-16 53
M3 5422 04-10-14 07-10-14 35
M5 4535 10-10-16 12-11-16 54
M9 5522 03-05-15 07-10-15 55
M3 5422 04-10-14 02-12-16 83
M5 4534 10-10-16 13-12-16 73
M3 5432 04-10-14 09-10-14 35
M3 5484 04-10-14 11-10-16 34
M3 5453 04-10-14 07-11-16 67
M3 5474 04-10-14 09-11-16 56
M3 5493 04-10-14 07-12-16 52
M3 5452 04-10-14 10-12-16 75
M3 5496 04-10-14 11-12-16 34
M3 5442 04-10-14 13-12-16 90
M3 5494 04-10-14 14-12-16 757
M3 5464 04-10-14 16-12-16 72
M5 4542 10-10-16 15-12-16 76
M5 4502 10-10-16 17-12-16 72
M5 4535 10-10-16 18-12-16 43
Output:
Member No Order Count (In last 90 days) Score
M1 0 1
M2 10 5
M3 9 5
M5 5 3
M9 0 1
[Note:
M1 is 0 because the DateCreated is less than 90 days from todays date (not a new member) and the member hasn't transacted in last 90
days
M2 order count is 10 because the DaetCreated is within 90 days from todays date (new member), also transaction date falls within 90
days so apply the formula (No of
transactions / Days being a member) x 90) = (1/10)*90 = 10 which is ordercount > 8+ hence score of 5.
M3 order count is 9 as the member has transacted 9 times in last 90 days. So score of 5.
M5 order count is 5 because the DaetCreated is within 90 days from todays date (new member), also his transaction date falls within
90 days so apply the formula (No of transactions / Days being a
member) x 90) = (4/72)*90 = 5 which is ordercount in range 4-6 hence
score of 3.
Let me know if any queries.
I believe this is a basic aggregation query, with some additional logic:
select memberno,
order_count_90_days,
(case when days_ago_start >= 90
then (case when order_count_90_days > 8 then 5
when order_count_90_days > 6 then 4
when order_count_90_days > 4 then 3
when order_count_90_days > 2 then 2
else 1
end)
else (case when (order_count_90_days * 90.0 / days_ago_start) > 8 then 5
when (order_count_90_days * 90.0 / days_ago_start) > 6 then 4
when (order_count_90_days * 90.0 / days_ago_start) > 4 then 3
when (order_count_90_days * 90.0 / days_ago_start) > 2 then 2
else 1
end)
end) as score
from (select memberno,
datediff(day, min(transactiondate), getdate()) as days_ago_start,
sum(case when transactiondate >= getdate() - 90 then 1 else 0 end) as order_count_90_days
from sample
group by memberno
) m;
The following is entirely untested, but should at least give you an idea of the process that you could follow to get it working.
I suggest you get each step executing and try to understand what they do and how.
Step one: count transactions in the last 90 days.
select Member_No, count(Order_No) as TransactionCount
from Sales
where datediff(day, Transaction_Date, getdate()) <= 90
group by Member_No
Step two: get start date and age per member
select
Member_No,
min(Date_Created) as Date_Created,
datediff(day, min(Date_Created), getdate()) as Days_Old
from Sales
group by Member_No
Step three: combine the above
select *
from (
select
Member_No,
min(Date_Created) as Date_Created,
datediff(day, min(Date_Created), getdate()) as Days_Old
from Sales
group by Member_No
) dc
join (
select Member_No, count(Order_No) as TransactionCount
from Sales
where datediff(day, Transaction_Date, getdate()) <= 90
) tc on tc.Member_No=dc.Member_No
Step four: Pro-rate transaction count for new members
select
Member_No,
case when dc.Days_Old > 90
then tc.Transaction_Count
else tc.Transaction_Count * 90 / dc.Days_Old
end as Transaction_Count
from (
select
Member_No,
min(Date_Created) as Date_Created,
datediff(day, min(Date_Created), getdate()) as Days_Old
from Sales
group by Member_No
) dc
join (
select Member_No, count(Order_No) as TransactionCount
from Sales
where datediff(day, Transaction_Date, getdate()) <= 90
) tc on tc.Member_No=dc.Member_No
Step five, add a score based on transaction count (note that I changed your score table so that it doesn't overlap):
select
*,
case
when x.Transaction_Count > 8 then 5
when x.Transaction_Count between 6 and 7 then 4
when x.Transaction_Count between 4 and 5 then 3
when x.Transaction_Count between 2 and 3 then 2
else 1
end as Score
from (
select
Member_No,
case when dc.Days_Old > 90
then tc.Transaction_Count
else tc.Transaction_Count * 90 / dc.Days_Old
end as Transaction_Count
from (
select
Member_No,
min(Date_Created) as Date_Created,
datediff(day, min(Date_Created), getdate()) as Days_Old
from Sales
group by Member_No
) dc
join (
select Member_No, count(Order_No) as TransactionCount
from Sales
where datediff(day, Transaction_Date, getdate()) <= 90
) tc on tc.Member_No=dc.Member_No
) x

Increment row depending on value of another column

I have a sql query below, where dtMontno could start from any month and am adding Row column manually as below :
SELECT COUNT(*) as count,
MONTH(TourTbl.DT_Started) as dtMonthno,
DATENAME(YYYY, TourTbl.DT_Started) as dtYear,
row_number() over (order by DATENAME(YYYY, TourTbl.DT_Started) asc,
MONTH(TourTbl.DT_Started) asc ) as Row
FROM TourTbl
INNER JOIN BranchTbl ON TourTbl.BranchID = BranchTbl.BranchID
INNER JOIN AgencyTbl on AgencyTbl.AgencyID = BranchTbl.AgencyID
WHERE Cancelled = 0 AND
(TourTbl.DT_Started >= '2010/03/15' and
TourTbl.DT_Started <= '2012/03/15') AND
AgencyTbl.AgencyID in ( 245 ) and
BranchRODID > 0
group by datename(M, TourTbl.DT_Started),
DATENAME(YYYY, TourTbl.DT_Started),
MONTH(TourTbl.DT_Started)
order by dtYear asc, dtMonthno asc
now my result is :
count dtMonthno dtYear Row
6 5 2011 1
8 6 2011 2
2 7 2011 3
23 8 2011 4
126 9 2011 5
101 10 2011 6
85 11 2011 7
92 12 2011 8
115 1 2012 9
102 2 2012 10
48 3 2012 11
Is there any way to start the Row column depending on the dtMonthno and increment by one in the example above would start from 5 and end in 15?
Thanks
Try changing the derivation of Row to:
row_number() over (order by YEAR(TourTbl.DT_Started) asc,
MONTH(TourTbl.DT_Started) asc ) +
min(YEAR(TourTbl.DT_Started)*12+MONTH(TourTbl.DT_Started)-1) OVER () % 12 as Row
You can add month of first DT_Started date:
SELECT COUNT(*) as count,
MONTH(TourTbl.DT_Started) as dtMonthno,
DATENAME(YYYY, TourTbl.DT_Started) as dtYear,
row_number() over (order by DATENAME(YYYY, TourTbl.DT_Started) asc,
MONTH(TourTbl.DT_Started) asc )
+ substring(min(DATENAME(YYYY, [TourTbl].DT_Started) + right ('0' + str (MONTH([TourTbl].DT_Started), 2), 2)) over (), 5, 2) - 1 as Row
FROM TourTbl
INNER JOIN BranchTbl ON TourTbl.BranchID = BranchTbl.BranchID
INNER JOIN AgencyTbl on AgencyTbl.AgencyID = BranchTbl.AgencyID
WHERE Cancelled = 0 AND
(TourTbl.DT_Started >= '2010/03/15' and
TourTbl.DT_Started <= '2012/03/15') AND
AgencyTbl.AgencyID in ( 245 ) and
BranchRODID > 0
group by datename(M, TourTbl.DT_Started),
DATENAME(YYYY, TourTbl.DT_Started),
MONTH(TourTbl.DT_Started)
order by dtYear asc, dtMonthno asc
I would truncate the dates to months and group by those values, then obtain years, months and row numbers based on the truncated dates:
SELECT
COUNT(*) AS count,
MONTH(GroupMonth) AS dtMonthno,
DATENAME(YYYY, GroupMonth) AS dtYear, /* why do you want year as a string? */
ROW_NUMBER() OVER (ORDER BY GroupMonth) + MONTH(MIN(GroupMonth) OVER ()) - 1 AS Row
FROM (
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, TourTbl.DT_Started), 0) AS GroupMonth
FROM TourTbl
INNER JOIN BranchTbl ON TourTbl.BranchID = BranchTbl.BranchID
INNER JOIN AgencyTbl on AgencyTbl.AgencyID = BranchTbl.AgencyID
WHERE Cancelled = 0 AND
(TourTbl.DT_Started >= '2010/03/15' and
TourTbl.DT_Started <= '2012/03/15') AND
AgencyTbl.AgencyID in ( 245 ) and
BranchRODID > 0
) s
GROUP BY GroupMonth