sql for Access Database - sql

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
)

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

How to do this in SQL (PostgreSQL Window Function?)

I have a situation in SQL (PostgreSQL specifically) that I'm struggling with. The schema/model that I'm working with is not under my control and not something I'm able to alter, so I am trying to figure out the best way to deal with the cards I've been dealt.
First, the schema, simplified for this question, but essentially it's invoice (Type = T) and transaction (Type <> T) lines combined into the same table. There can and will be n-number of tranaction lines per invoice and n-number of invoices per client.
Id
Type
InvoiceNo
ClientId
100
I
100
1
99
X
0
1
98
S
0
1
97
T
0
1
96
I
99
1
95
X
0
1
94
S
0
1
What I ultimately would like to end up with is something like the below, with the Invoice (Type = I) records removed and the Transaction (Type <> T) records that fall after each Invoice record populated with it's corresponding InvoiceId value.
Id
Type
InvoiceNo
ClientId
99
X
100
1
98
S
100
1
97
T
100
1
95
X
99
1
94
S
99
1
So far, the closest I've been able to get, which isn't very close, is using the below SQL:
select
t1.Id,
t1.Type,
t2.InvoiceNo,
t1.ClientId
from table AS t1
join (select
Id,
InvoiceNo,
ClientId
from table
where type = 'I') as t2
on t1.ClientId = t2.ClientId
where t1.ClientId = t2.ClientId and t1.Id <= t2.Id and t1.Type <> 'I'
The result of that looks something like the below, which works fine for the first invoice per client and then creates extra transaction records for each invoice
Id
Type
InvoiceNo
ClientId
99
X
100
1
98
S
100
1
97
T
100
1
95
X
100
1
95
X
99
1
94
S
100
1
94
S
99
1
Any help or guidance is much appreciated!
** Updated with more complex example **
Source:
Id
Type
InvoiceNo
ClientId
1
X
0
1
2
I
97
1
3
S
0
2
4
X
0
2
5
S
0
1
6
I
98
2
7
S
0
1
8
X
0
1
9
I
99
1
10
T
0
1
11
S
0
1
12
X
0
1
13
I
100
1
Playing with the answer below, I came up with:
select * from (select t.*,
max(InvoiceNo) filter (where type = 'I') over (partition by clientid order by id DESC) as imputed_invoiceno
from t) as x
where Type <> 'I';
Which gets me close:
Id
Type
InvoiceNo
ClientId
imputed_invoiceno
12
X
0
1
100
11
S
0
1
100
10
T
0
1
100
8
X
0
1
99
7
S
0
1
99
5
S
0
1
99
1
X
0
1
99
4
X
0
2
98
3
S
0
2
98
Best case result:
Id
Type
InvoiceNo
ClientId
12
X
100
1
11
S
100
1
10
T
100
1
8
X
99
1
7
S
99
1
5
S
99
1
1
X
97
1
4
X
98
2
3
S
98
2
Based on your sample data, you can use a cumulative window function:
select t.*,
min(invoiceno) filter (where type = 'I') over (order by id desc) as imputed_invoiceno
from t;

Count distinct values of a Column based on Distinct values of First Column

I am dealing with a huge volume of traffic data. I want to identify the vehicles which have changed their lanes, I'm Microsoft Access with VB.Net.
Traffic Data:
Vehicle_ID Lane_ID Frame_ID Distance
1 2 12 100
1 2 13 103
1 2 14 105
2 1 16 130
2 1 17 135
2 2 18 136
3 1 19 140
3 2 20 141
I have tried to distinct the Vehicle_ID and then count(distinct Lane_ID).
I could list the distinct Vehicle_ID but the it counts the total Lane_ID instead of Distinct Lane_ID.
SELECT
Distinct Vehicle_ID, count(Lane_ID)
FROM Table1
GROUP BY Vehicle_ID
Shown Result:
Vehicle_ID Lane Count
1 3
2 3
3 2
Correct Result:
Vehicle_ID Lane Count
1 1
2 2
3 2
Further to that i would like to get all Vehicle_ID who have changed their lane (all data including previous lane and new lane). Output result would be somehow like: Vehicle_ID Lane_ID Frame_ID Distance
2 1 17 135
2 2 18 136
3 1 19 140
3 2 20 141
Access does not support COUNT(DISTINCT columnname) so do this:
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
So
to identify the vehicles which have changed their lanes
you need to add to the above query:
HAVING COUNT(t.Lane_ID) > 1
SELECT
Table1.Vehicle_ID,
LANE_COUNT
FROM Table1
JOIN (
SELECT Vehicle_ID, COUNT(*) as LANE_COUNT FROM (
SELECT distinct Vehicle_ID, Lane_ID FROM Table1
) dTable1 # distinct vehicle and land id
GROUP BY Vehicle_ID # counting the distinct
) cTable1 ON cTable1.Vehicle_ID = Table1.Vehicle_ID # join the table with the counting
I think you should do one by one,
Distinct the vehicle id and land id
counting the distinct combination
and merge the result with the actual table.
If you want vehicles that have changed their lanes, then you can do:
SELECT Vehicle_ID,
IIF(MIN(Lane_ID) = MAX(Lane_ID), 0, 1) as change_lane_flag
FROM Table1
GROUP BY Vehicle_ID;
I think this is as good as counting the number of distinct lanes, because you are not counting actual "lane changes". So this would return "2" even though the vehicle changes lanes multiple times:
2 1 16 130
2 1 17 135
2 2 18 136
2 1 16 140
2 1 17 145
2 2 18 146

SELECT clause with SUM condition

Have this table :
//TEST
NUMBER TOTAL
----------------------------
1 158
2 355
3 455
//TEST1
NUMBER QUANTITY UNITPRICE
--------------------------------------------
1 3 5
1 3 6
1 3 4
2 4 8
3 5 4
I used following query:
SELECT t.NUMBER,sum(t.TOTAL),NVL(SUM(t2.quantity*t2.unitprice),0)
FROM test t INNER JOIN test1 t2 ON t.NUMBER=t2.NUMBER
GROUP BY t.NUMBER;
OUTPUT:
NUMBER SUM(TOTAL) SUM(t2.quantity*t2.unitprice)
-----------------------------------------------------------
1 474 45 <--- only this wrong
2 355 32
It seem like loop for three times so 158*3 in the record.
EXPECTED OUTPUT:
NUMBER SUM(TOTAL) SUM(t2.quantity*t2.unitprice)
-----------------------------------------------------------
1 158 45
2 355 32
You have to understand that the result of your join is something like this:
//TEST1
NUMBER QUANTITY UNITPRICE TOTAL
--------------------------------------------------------------
1 3 5 158
1 3 6 158
1 3 4 158
2 4 8 355
3 5 4 455
It means you don't need to apply a SUM on TOTAL
SELECT t.NUMBER,t.TOTAL,NVL(SUM(t2.quantity*t2.unitprice),0)
FROM test t INNER JOIN test1 t2 ON t.NUMBER=t2.NUMBER
GROUP BY t.NUMBER, t.TOTAL;
Something like this should work using a subquery separating the sums:
select t.num,
sum(t.total),
test1sum
from test t
join (
select num, sum(qty*unitprice) test1sum
from test1
group by num
) t2 on t.num = t2.num
group by t.num, test1sum
SQL Fiddle Demo
In regards to your sample data, you may not even need the additional group by on the test total field. If that table only contains distinct ids, then this would work the same:
select t.num,
t.total,
sum(qty*unitprice)
from test t
join test1 t2 on t.num = t2.num
group by t.num, t.total

SQL sum and link fields in multiple tables

Table 1
prodUID (*) plantID ItemName size qty
1 1 car med 5
2 1 car small 2
3 1 car large 8
4 1 truck small 7
5 1 truck med 0
6 1 truck large 4
7 1 van small 0
8 2 truck large 10
table2
UID(*) plantID table2_plan_tid table2_prodUID itemname size num wk
------------------------------------------------------------------------------
1 1 1 car med 3 41
2 1 2 car small 0 42
3 1 3 car large 6 41
4 1 4 truck small 1 44
5 1 5 truck med 10 45
6 1 6 truck large 1 43
7 1 7 van small 7 42
8 2 8 car med 10 41
table3
UID(*) plantid table3_wk table3_prodUID itemName size qty
------------------------------------------------------------------
1 1 41 2 car med 5
2 1 41 3 car large 7
3 1 43 7 van small 8
Result I'm trying to get is
for plantid = ? and wk between ? and ?
plantid = 1 and wk between 41 and 45
sum qty for table1 on plantID
sum wk for table2 on plantID and for weeks
sum qty on table with on plantID and for weeks
and some how link this with joins or subqueries to get
plantid Itemname qty num order
------------------------------------
1 car 15 9 12
1 truck 11 12 0
1 van 0 7 8
I can't seem to get the right outcome
I also considered putting in an itemNameID field in all tables
if that would make it easier
You can achieve what you want using this query:
SELECT table1.plantID, table1.ItemName, SUM(table1.qty), COALESCE(t2.num_sum, 0), COALESCE(t3.qty_sum, 0)
FROM table1
LEFT JOIN (SELECT plantid, itemname, SUM(num) AS num_sum
FROM table2
WHERE wk >= 41 AND wk <= 45
GROUP BY plantID, itemname) t2 ON table1.plantID = t2.plantID AND table1.ItemName = t2.ItemName
LEFT JOIN (SELECT plantid, itemname, SUM(qty) AS qty_sum
FROM table3
WHERE table3_wk >= 41 AND table3_wk <= 45
GROUP BY plantID, itemname) t3 ON table1.plantID = t3.plantID AND table1.ItemName = t3.ItemName
WHERE table1.plantid = 1
GROUP BY plantID, ItemName
See also live!
Basically, you join 3 queries in order to get what you want.
That would be a bit cleaner if you respect a certain harmony with you column names.