How to check id-s of parents and then set value - sql

I have table like this :
ID object_id parent_id allowed
1 1 0 0
2 23 25 1
3 25 44 0
4 44 38 0
5 38 1 0
6 52 55 1
7 55 58 0
8 58 60 0
9 60 1 0
Now want select row-s where allowed = 1 and then set allowed = 1 for parents of the row which i select. For example it will be like :
step 1. select object_id , parent_id from myTbl where allowed = 1 Displays:
ID object_id parent_id allowed
2 23 25 1
6 52 55 1
step 2: It checks if the object_id is IN the parent_id from the above result and sets allowed = 1 when the object_id is equal to any of the parent_id's.
The exact same step2 repeats until it reaches a point where there is no match between object_id and parent_id
ID object_id parent_id allowed
2 23 25 1
6 52 55 1
3 25 44 0 --update to 1
7 55 58 0 -- update to 1
The exact same principle is being applied to the folling records, too:
for 25,44,1 - 44,38,0 (allowed is 0 want set 1) when set allowed = 1 it will be
44,38,1
for 55,58,1 - 58,60,0 (allowed is 0 want set 1) when set allowed = 1 it will be
58,60,1
How to do it ? In table My table contains multiple records with status allowed=1 and only 2 of them are used in this particular example.

Try:
UPDATE tbl
SET allowed = 1
FROM (SELECT *
FROM tbl
WHERE allowed = 0) A
INNER JOIN
(SELECT *
FROM tbl
WHERE allowed = 1) B
ON A.objectid = B.parentid

Related

Select values that are within a certain range but exclude the values that determine the range in Sqlite

In a SQLite local database, I have a 2 column-table, one containing values, and one containing categories. I want to update the categories of certain rows based on the following selection:
select the rows that are in a certain category
determine the values for those rows.
select the rows that have values within a certain range of the values of the already selected rows.
update the rows that are within the second selection, but exclude those that are in the first selection.
The statement that I have now (that does not work) is as follows:
UPDATE table SET category = '3' WHERE
(
value BETWEEN
(
((SELECT value FROM table WHERE category = '2') +4)
AND
((SELECT value FROM table WHERE category = '2') -4)
EXCEPT SELECT value FROM table WHERE category = '2'
)
... (further constraints)
)
This runs without error, but does not actually appear to select anything, as nothing is updated. What is the correct way to get this update to work?
EDIT: as requested an example with tables:
rowid
Value
Category
1
20
2
2
30
2
3
40
2
4
70
2
5
5
1
6
19
1
7
26
1
8
42
1
9
49
1
10
52
1
11
71
1
12
90
1
I want the values of the rows that are currently in category 1, to be placed in category 3, based on a range of 4 around the values of the rows that are in category 2. So in this case any row that has category = 1, that has a value of either 16-24, 26-34, 36-44 or 66-74.
rowid
Value
Category
1
20
2
2
30
2
3
40
2
4
70
2
5
5
1
6
19
3
7
26
3
8
42
3
9
49
1
10
52
1
11
71
3
12
90
1
You can use EXISTS:
UPDATE tablename
SET Category = 3
WHERE Category = 1
AND EXISTS (
SELECT 1
FROM tablename t
WHERE t.Category = 2
AND tablename.Value BETWEEN t.Value - 4 AND t.Value + 4
);
See the demo.

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;

SQL merge 3 tables

I have an sql query involving 2 tables and try to add a third one.
These are the tables
FreeBookPos
FreeBooK_ID
ArticleNr
Amount
FreeBook
ID
BookNr
Date
FreeFields
FreeFieldType
Value
SQLPrimeKey
The first two are linked this way
select FreeBookPos.ArticleNr, Format(FreeBooking.Date, 'yyyy_MM') as dt,
SUM(CASE WHEN FreeBook.BookNr = 0 THEN FreeBookPos.Amount ELSE 0 END) as TotalEntryAmount,
SUM(CASE WHEN FreeBook.BookNr = 1 THEN FreeBookPos.Amount ELSE 0 END) as TotalLeftAmount
From FreeBookPos
INNER JOIN FreeBook on FreeBookPos.FreeBook_ID = FreeBook.ID
group by FORMAT ( FreeBook.Date, 'yyyy_MM'), FreeBookPos.ArticleNr
order by dt, ArticleNr
Now I need to add the table 3. This table is linked via SQLPrimeKey to FeeBook table ID. I then need to have only the fields where FreeFields.Value 2 or 4 and FreeFields.FreeFieldType = 54.
I tried various options with join but never get the result. Would I need to first join table 2 and 3 and then with 1 in a separate step?
Table 1: FreeBookPos
FreeBook_ID ArticleNr Amount
1 145 12
2 145 6
3 143 4
4 145 1
5 145 42
Table 2: FreeBook
ID BookNr Date
1 1 2012-05-19
2 -1 2012-05-21
3 1 2012-05-22
4 -1 2012-05-24
5 -1 2012-06-25
Table 3: FreeFields
SQLPrimareyKey FreeFieldType Value
1 54 1
2 52 2
3 54 4
4 54 2
5 54 2
Result should be:
ArticleNr Dt TotalEntryAmount TotalLeftAmount
143 2012-05 4 0
145 2012-05 0 -1
145 2012-06 0 -42
Try the below -
select FreeBookPos.ArticleNr, Format(FreeBooking.Date, 'yyyy_MM') as dt,
SUM(CASE WHEN FreeBook.BookNr = 0 THEN FreeBookPos.Amount ELSE 0 END) as TotalEntryAmount,
SUM(CASE WHEN FreeBook.BookNr = 1 THEN FreeBookPos.Amount ELSE 0 END) as TotalLeftAmount
From FreeBookPos
INNER JOIN FreeBook on FreeBookPos.FreeBook_ID = FreeBook.ID
inner join FreeFields on FreeBook.ID=SQLPrimareyKey
where value in (2,4) and FreeFieldType = 54
group by FORMAT ( FreeBook.Date, 'yyyy_MM'), FreeBookPos.ArticleNr
order by dt, ArticleNr

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
)

find unmatched data

column Role _id has13 and 22 as there id, need to fetch the unmatched ID from column cmat_customer_id.
see below data for reference"
SYSTEM_SERIAL_NUMBER ROLE_ID TOTAL_ROLE_ID CMAT_CUSTOMER_ID CMAT_SITE_ID
200000301498 13 1 6082581 -999999
200000304907 13 1 5018171 -999999
200000273689 13 1 5008520 -999999
200000280436 13 1 5008520 -999999
451507000097 22 1 5013865 -999999
451448000036 13 1 5008621 5008622
451507000148 22 1 5013459 -999999
FB6500N120181 13 1 5002239 6019184
FB6500N120181 22 1 5002239 6019184
451507000097 22 1 5013865 5013867
451507000097 13 1 5013865 -999999
Expected resulet
SYSTEM_SERIAL_NUMBER ROLE_ID count_ROLE_ID CMAT_CUSTOMER_ID CMAT_SITE_ID
200000301498 13 1 6082581 -999999
200000301498 22 1 5018171 -999999
200000304907 13 1 5018172 -999999
200000304907 22 1 5008520 -999999
451507000148 13 1 5008512 -999999
we do not want below result set
SYSTEM_SERIAL_NUMBER ROLE_ID TOTAL_ROLE_ID CMAT_CUSTOMER_ID CMAT_SITE_ID
451449000141 13 1 6097038 9409647
451449000141 22 1 6097038 9409647
One method is to use not exists:
select t.*
from t
where (role_id = 13 and
not exists (select 1 from t t2 where t2.cmat_customer_id = t.cmat_customer_id and t2.role_id = 22)
) or
(role_id = 22 and
not exists (select 1 from t t2 where t2.cmat_customer_id = t.cmat_customer_id and t2.role_id = 13)
);