Select last value in each group - sql

In database I have two table. I want to join two tables and get last row each SensorType
Sensors table:
Id
CodeNumber
PortNumber
SensorType
IsActive
13
Dht11
3
0
1
14
Dht11
3
2
1
17
Global
100
4
1
18
Dht11
3
1
1
19
Dht11
3
3
1
SensorsValue table (SensorId is foreign key Sensors table):
Id
SensorId
Value
DateOfRetrevingValue
19
13
25
2021-07-23 08:50:27.0000000
20
14
45
2021-07-23 09:50:27.0000000
21
17
12
2021-07-23 10:50:27.0000000
22
18
24
2021-07-23 11:50:27.0000000
23
19
45
2021-07-23 12:50:27.0000000
24
13
23
2021-07-23 13:50:27.0000000
25
14
56
2021-07-23 14:50:27.0000000
26
17
23
2021-07-23 15:50:27.0000000
27
18
34
2021-07-23 16:50:27.0000000
28
19
23
2021-07-23 17:50:27.0000000
I want to join two tables and select SensorType from Sensors table and last Value (from SensorsValue) each SensorType. In this case i want get result:
|SensorType|Value|
|----------|-----|
| 0 | 23 |
| 2 | 56 |
| 4 | 23 |
| 1 | 34 |
| 3 | 13 |
I found this post and use this statement:
SELECT distinct Sensor.Type, MAX(SensorValues.Id), SensorValues.Value
FROM Sensor
INNER JOIN SensorValues ON Sensor.Id=SensorValues.SensorId
GROUP BY Sensor.Type
but I got error:
Column 'SensorValues.Value' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
If I use this statement without SensorValues.Value query work, but I got result:
|SensorType|Id|
|----------|--|

you should use first_value
select distinct t1.SensorType,
first_value(t2.value) over(partition by t1.SensorType order by t2.DateOfRetrevingValue desc)
from Sensors t1,
SensorsValue t2
where t1.id = t2.SensorId

This is a very handy place to use apply:
select s.*, sv.value, sv.DateOfRetrevingValue
from sensors s cross apply
(select top (1) sv.*
from SensorValues sv
where s.Id = sv.SensorId
order by sv.DateOfRetrevingValue desc
) sv;
One advantage of this approach is that you can returns as many columns as you like. I also suspect that that performance will be better than a select distinct approach.

You can try this
SELECT Sensor.Type, SensorValues.Value
FROM Sensor
INNER JOIN
(
SELECT MAX(ID)Id, SensorId FROM SensorValues GROUP BY SensorId
)mId ON Sensor.Id=mId.SensorId
INNER JOIN SensorValues ON mId.Id=SensorValues.Id

Related

T-SQL How to configure Group by so that specific values would be correctly shown

My current T-SQL query provides the following results:
Query:
WITH CTE AS
(
SELECT SubscriberID, sum(valueMB) as ValuesMB
FROM dbo.InternetNetwork
GROUP BY SubscriberID
),
CTE2 AS (
SELECT ab.planID, a.SubscriberID, MAX(ValuesMB) as MaximumValue
FROM CTE AS a
left join
Subscriber as ab on a.SubscriberID= ab.SubscriberID
GROUP BY ab.planID, a.SubscriberID
)
select *
FROM CTE2 as b
ORDER BY b.MaximumValue desc
Output:
planID | SubscriberID | MaxValue
19 1555 97536.00
18 3528 97478.00
2 4029 93413.00
Query #2:
WITH CTE AS
(
SELECT SubscriberID, sum(valueMB) as ValuesMB
FROM dbo.InternetNetwork
GROUP BY SubscriberID
),
CTE2 AS(
SELECT ab.planID, MAX(ValuesMB) as MaximumValue
FROM CTE AS a
left join
Subscriber as ab on a.SubscriberID= ab.SubscriberID
GROUP BY ab.planID
)
SELECT pl.OperatorID, MAX(b.MaximumValue) as Super
FROM CTE2 as b
left join
Plan as pl on b.planID= pl.planID
GROUP BY pl.operatorID
ORDER BY pl.operatorID
Output #2:
OperatorID | Value
1 93413.00
2 86017.00
3 97536.00
I would like to also include a subscriberID, but I'm unable to figure out a way to do so, as the only way to do it, is including in the last SELECT and adding to GROUP BY, which when done, makes a mess of a result which is not accurate.
My desired output:
OperatorID | Value | SubscriberID
1 93413.00 4029
2 86017.00 164
3 97536.00 1544
internet network data:
SubscriberID ValuesMB
1 28
1 27
2 27
2 27
2 27
3 29
3 28
3 27
3 27
4 27
4 27
4 29
Subscriber Data:
SubscriberID PersonID PlanID
1 1 3
2 2 10
3 2 6
4 3 14
5 3 1
6 4 18
7 5 5
8 5 1
9 5 9
10 5 16
11 6 13
12 6 13
13 6 20
14 6 16
15 7 4
Plan data
PlanID OperatorID
1 1
2 1
3 2
4 2
5 2
6 2
7 2
8 2
9 2
10 2
11 2
12 3
13 3
14 3
15 3
16 3
17 3
18 3
19 3
20 3
The tables are somewhat like this related InternetNetwork-> Subscriber -> Plan. InternetNetwork contains how much each Subscribed has used. Each Subscriber has Plan associated with him. Each Plan contains a different Operator, there are only three. I wish to list all three operators, the data transferred by the subscriber of the plan that has the operator and Subscriber ID.
Window functions allow you to have fields in your select along with aggregate functions. You can do something like this
;WITH CTE AS
(
SELECT I.SubscriberID,
S.PlanID,
SUM(ValuesMB) OVER(PARTITION BY i.SubscriberID)as ValuesMB
FROM dbo.InternetNetwork I
JOIN Subscriber S
ON I.SubscriberID = S.SubscriberID
),
CTE2 AS
(
SELECT p.operatorID,
a.SubscriberID,
a.ValuesMB,
ROW_NUMBER() OVER(PARTITION BY p.operatorID ORDER BY a.ValuesMB DESC) as rn
FROM CTE a
join [Plan] P
on a.planID = P.planID
)
SELECT operatorID,
ValuesMB,
SubscriberID
FROM CTE2
where rn = 1

Issue with SQL Group By and COALESCE on sqlite

I have a table as below in sqlite database. I want to create a line chart showing usage by product groups.
Table: ProductUsageData
UserID ProductName ProductGroup Qty RecordID
1 A1 A 12 1
2 A1 A 12 1
1 A2 A 15 1
3 A1 A 12 2
2 B1 B 12 2
5 B2 B 5 2
1 A1 A 12 3
1 A2 A 15 3
4 A1 A 12 3
3 C1 C 12 3
2 C2 C 15 3
Since I want separate line for each ProductGroup I am using below Query
SELECT
SUM(Qty) as UsedQty,
ProductGroup,
RecordID
FROM ProductUsageData
GROUP BY ProductGroup, RecordID
ORDER BY RecordID ASC;
While I get three records for A (for each RecordID) I get only 1 record each for B & C as they are not used during each RecordID.
Problem is when I am putting one line for each ProductGroup in the chart, the points for B & C are shown as per Qty in the first
My output is like this
A 39 1
A 12 2
B 17 2
A 39 3
C 27 3
So the graph looks like this
instead of
To fix this I changed the query using COALESCE to get 0 Qty if the ProductGroup is not used during each recording.
SELECT
COALESCE(SUM(Qty), 0) as UsedQty,
ProductGroup,
RecordID
FROM ProductUsageData
GROUP BY ProductGroup, RecordID
ORDER BY RecordID ASC;
I was expecting output as below
A 39 1
B 0 1
C 0 1
A 12 2
B 17 2
C 0 2
A 39 3
B 0 3
C 27 3
But I am getting same output as first
Please let me know how can I correct the query to get desired output
A typical solution is to first cross join two queries that select the distinct product groups and record ids from the table; this gives you all possible combinations of productGroup and recordID.
Then, you can bring in the original table with a left join, and aggregate:
select
g.productGroup,
coalesce(sum(p.qty), 0) qty,
r.recordID
from (select distinct productGroup from productUsageData) g
cross join (select distinct recordID from productUsageData) r
left join productUsageData p
on p.productGroup = g.productGroup
and p.recordID = r.recordID
group by r.recordID, g.productGroup
order by r.recordID, g.productGroup
In the real world, you might have separate referential tables for product groups and records ids, which would make the query simpler and more efficient (since it would avoid the need to select distinct in subqueries).
Demo on DB Fiddle:
productGroup | qty | recordID
:----------- | :-- | :-------
A | 39 | 1
B | 0 | 1
C | 0 | 1
A | 12 | 2
B | 17 | 2
C | 0 | 2
A | 39 | 3
B | 0 | 3
C | 27 | 3

sql for Access Database

I am dealing with a huge volume of traffic data. I want to identify the vehicles which have changed their lanes in MS Access database. I want to identify those records only which has changed the lane (immediate two records: before lane change and after lane change)
Traffic Data:
Vehicle_ID Lane_ID Frame_ID Distance
1 2 12 100
1 2 13 103
1 2 14 105
2 1 15 107
***2 1 16 130
2 2 17 135***
2 2 18 136
***3 1 19 140
3 2 20 141***
3 2 21 147
4 2 22 149
***4 2 23 151
4 1 24 154***
4 1 25 159
With assistance from here i have sorted out those Vehicle_ID which have changed their lanes:
SELECT t.Vehicle_ID, COUNT(t.Lane_ID) AS [Lane Count]
FROM (
SELECT DISTINCT Vehicle_ID, Lane_ID FROM Table1
) AS t
GROUP BY t.Vehicle_ID
HAVING COUNT(t.Lane_ID) > 1
Shown Result:
Vehicle_ID Lane Count
2 2
3 2
4 2
Now i want to do further analysis withe records of lane changing by segregating immediate two records: before and after lane change. My desired output would be:
Desired Result:
Vehicle_ID Lane_ID Frame_ID Distance
***2 1 16 130
2 2 17 135***
***3 1 19 140
3 2 20 141***
***4 2 23 151
4 1 24 154***
Assuming the frame ids have no gaps, you can do this using joins:
select t1.*
from (table1 as t1 inner join
table1 as t1prev
on t1prev.Vehicle_ID = t1.Vehicle_ID and
t1prev.frame_id = t1.frame_id - 1
) inner join
table1 as t1next
on t1next.Vehicle_ID = t1.Vehicle_ID and
t1next.frame_id = t1.frame_id + 1
where t1prev.lane_id <> t1.lane_id or
t1next.lane_id <> t1.lane_id;
Otherwise, this will be a very expensive query.
You can do it with EXISTS:
select t.* from Table1 t
where
exists (
select 1 from Table1
where
vehicle_id = t.vehicle_id
and
frame_id in (t.frame_id - 1, t.frame_id + 1)
and
lane_id <> t.lane_id
)

Join two tables get count without using any where condition

I am trying to join two tables, prcshd (head table) and prcsdt (detail table), and need to get count of prod_prcshd_id in prcsdt (detail table) without using where (if required can use sub-query). Not sure ...
Tried like
select distinct count(b.prod_prcshd_id), b.prod_prcshd_id
from tra_pharmacy_prod_prcshd a join
tra_pharmacy_prod_prcsdt b
on b.prod_prcshd_id = a.id
group by b.dt_id
My tables:
prcshd (head table)
id(pk) | medi_name_id | med_prep_id
1 83 1
2 83 2
prcsdt (detail table)
dt_id(pk) | prod_prcshd_id(fk) | type_id | prod_name_id |medi_prep_id
1 1 4 83 1
2 1 5 83 1
3 1 6 83 1
4 2 4 83 2
still no luck.
id | prod_prcshd_id | medi_name_id
1 3 83
2 1 83
I'm not sure if this will fix your problem. But, you almost never need select distinct with group by. I suspect you want one of these this:
select b.prod_prcshd_id, count(b.prod_prcshd_id)
from tra_pharmacy_prod_prcshd a join
tra_pharmacy_prod_prcsdt b
on b.prod_prcshd_id = a.id
group by b.prod_prcshd_id;
Under most circumstances, you don't even need the join:
select ppp.prod_prcshd_id, count(*)
from tra_pharmacy_prod_prcsdt ppp
group by ppp.prod_prcshd_id;

Simple data, Complex query on SQL Server

I need to make a query over an SQL Server table but I don't know exactly how.
Consider this table (the real table is much more complex, Ord1 and Ord2 are dates that could be null, but i simplified it to this case):
Data of MyTable
ID MaqID Ord1 Ord2
------------------------
1 144 4 3
2 144 2 1
3 12 2 3
4 144 3 5
5 12 3 1
6 144 4 2
7 12 2 4
8 144 2 3
9 12 1 5
10 12 3 2
I need records for specific MaqID in Specific Order. I get that with this Query:
SELECT * FROM myTable WHERE MaqID=144 ORDER BY MaqID, Order1 DESC, Order2
Wich give me:
ID MaqID Ord1 Ord2
------------------------
6 144 4 2
1 144 4 3
4 144 3 5
2 144 2 1
8 144 2 3
Now, I need a single query that, for each MaqID, return the first ID for each subquery following above order. The result should be:
Expected result
MaqID ID
-----------
144 6
12 5
I have already try distinct conbination of TOP a MAX, but TOP result only one result and i need one for each MaqID, and for Max I have not field to maximize.
To sumarize: I need the first ID for each MaqID from a subquery in a specific order
Any ideas? Thanks!
You can do this using row_number():
select t.*
from (select t.*,
row_number() over (partition by macid Order1 DESC, Order2) as seqnum
from mytable t
) t
where seqnum = 1;