Find 3 or more consecutive transaction record where the transaction amount greater than 100 and the records belong to the same category - sql

I have a customer transaction table which has 3 columns, id, Category, TranAmount. Now I want to find 3 or more consecutive transaction records which belongs to the same category and the TranAmount greater than 100.
Below is the sample table:
Id Category TranAmount
1 A 190
2 A 160
3 A 169
4 B 190
5 A 90
6 B 219
7 B 492
8 B 129
9 B 390
10 B 40
11 A 110
12 A 130
And the output should be:
Id Category TranAmount
1 A 190
2 A 160
3 A 169
6 B 219
7 B 492
8 B 129
9 B 390

Look into "gaps and islands" reference for a deeper understanding of the approach. Here's one of many you could read: https://www.red-gate.com/simple-talk/sql/t-sql-programming/the-sql-of-gaps-and-islands-in-sequences/
In this specific problem you have two conditions that cause a break in a consecutive series, those being a change in category or an amount that doesn't meet the threshold.
with data as (
select *,
row_number() over (order by Id) as rn,
row_number() over (partition by
Category, case when TranAmount >= 100 then 1 else 0 end order by Id) as cn
from Transactions
), grp as (
select *, count(*) over (partition by rn - cn) as num
from data
where TranAmount >= 100
)
select * from grp where num >= 3;
https://rextester.com/DUM44618

This will work if there are no gaps between the ids:
select distinct t.*
from tablename t inner join (
select t.id from tablename t
where t.tranamount > 100
and
exists (
select 1 from tablename
where id = t.id - 1 and category = t.category and tranamount > 100
)
and
exists (
select 1 from tablename
where id = t.id + 1 and category = t.category and tranamount > 100
)
) tt on t.id in (tt.id - 1, tt.id, tt.id + 1)
See the demo.
Results:
Id | Category | TranAmount
-: | :------- | ---------:
1 | A | 190
2 | A | 160
3 | A | 169
6 | B | 219
7 | B | 492
8 | B | 129
9 | B | 390

I can't really test this out yet but give this a try.
SELECT Id, Category, Amount FROM Table
WHERE Amount > 100
and Category IN
(SELECT Category FROM Table
WHERE Amount > 100
GROUP BY Category HAVING COUNT (Category ) >= 3)

Related

How to find values of column status that have 8 but still do not have 9

Iam working on project and i need to find in table one column named status (this col have values from 1-12), i want to find all values that below to the condition (if the status have 8 but still do not have 9), for more information :
Original table :
File_name id status Execution_number
Live_package1.zip 333 3 4444443434343
Live_package2.zip 444 7 8888888886767
Live_package1.zip 333 8 4444443434343
Live_package2.zip 444 8 8888888886767
Live_package2.zip 444 10 8888888886767
Live_package3.zip 666 8 9999999999999
Live_package4.zip 111 9 3333333333333
The desired result
File_name id status Execution_number
Live_package1.zip 333 8 4444443434343
Live_package3.zip 666 8 9999999999999
iam using the following code , but I did not received the result what I expected:
SELECT *
FROM tbl_doc_state
WHERE status =8
AND NOT(status !=9);
If you want to exclude rows that have status 8 but there are other rows with the same id but with the status 9, use a not exists condition:
SELECT t1.*
FROM tbl_doc_state t1
WHERE status = 8
and not exists (select *
from tbl_doc_state t2
where t2.id = t1.id
and t2.status >= 9);
Something like this?
SQL> select * From tbl_doc_state;
FIL ID STATUS EXECUTION_NUMBER
--- ---------- ---------- ----------------
lp1 333 3 43
lp2 444 7 67
lp1 333 8 43
lp2 444 10 67
lp3 666 8 99
lp4 111 9 33
6 rows selected.
SQL> with temp as
2 (select id,
3 max(status) max_status
4 from tbl_doc_state
5 group by id
6 )
7 select a.*
8 from tbl_doc_state a join temp t on a.id = t.id
9 and a.status = t.max_status
10 where t.max_status = 8;
FIL ID STATUS EXECUTION_NUMBER
--- ---------- ---------- ----------------
lp1 333 8 43
lp3 666 8 99
SQL>
You don't tell which column should used to order the records. If you are using status for this, then the query is just:
select * from tbl_doc_state where status = 8
If you have another ordering column, say sort_id, then the question makes more sense. You can use window functions:
select t.*
from (
select t.*, max(case when status = 9 then 1 else 0 end) over(partition by id order by sort_id) has_status_9
from mytable t
) t
where status = 8 and has_status_9 = 0
With window function MAX():
select t.File_name, t.id, t.status, t.Execution_number
from (
select t.*, max(t.status) over(partition by t.id) max_status
from tbl_doc_state t
) t
where max_status = 8 and status = max_status
See the demo.
Results:
> File_name | id | status | Execution_number
> :---------------- | --: | -----: | :---------------
> Live_package1.zip | 333 | 8 | 4444443434343
> Live_package3.zip | 666 | 8 | 9999999999999

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
)

complex paratition sum in postgresql

I have tables as follow:
A deliveries
delveryid clientid deliverydate
1 10 2015-01-01
2 10 2015-02-02
3 11 2015-04-08
B items in deliveris
itemid deliveryid qty status
70 1 5 1
70 1 8 2
70 2 10 1
72 1 12 1
70 3 100 1
I need to add a column to my query that gives me the qty of each part in other deliveris of the same client.
meaning that for given data of client 10 and delivery id 1 I need to show:
itemid qty status qtyOther
70 5 1 10 //itemid 70 exists in delivery 2
70 8 2 10 //itemid 70 exists in delivery 2
72 12 1 0 //itemid 72 doesn't exists in other delivery of client 11
Since I need to add qtyOther to my existing qry i'm trying to avoid using Group By as it's a huge query and if I use SUM in select I will have to group by all items in select.
This is what I have so far:
Select ....., coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid) ,0) AS qtyOther
FROM B b
LEFT JOIN A a USING
LEFT JOIN (other tables)
WHERE clientid=10 ....
This query gives me the total sum of qty per itemid for specific clientid, regardless of which delivery it is. How do I change it so it will consider the delivryid? I need something like:
coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid) FROM B where deliveryid<>b.deliveryid ,0) AS qtyOther
Any suggestions how to do that?
Note: I can NOT change the condition in WHERE.
I think you just want to subtract out the total for the current delivery:
Select .....,
(coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid), 0) -
coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid, a.deliveryid), 0)
) as qtyOther

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