Update seq based on criteria - Oracle SQL - sql

I have a table:
table1
id element_id element_value lx_seq line_num
59 A301-01 Test 1 25
59 A301-01 Test 1 26
59 K301-01 Test 1 27
59 K301-01 1 28
59 K302-01 18105678982800 1 28
59 K304-01 TOPS 1 28
59 K305-01 7 1 28
59 K306-01 888P 1 28
60 K301-01 Test 1 27
60 K301-01 1 28
60 K302-01 18105678982800 1 28
60 K304-01 TOPS 1 28
60 K305-01 7 1 28
60 K306-01 888P 1 28
61 K301-01 Test 1 27
61 K301-01 1 27
62 K301-01 Test 1 27
62 K301-01 1 28
62 K301-01 18105678982800 1 29
62 K304-01 TOPS 1 29
62 K305-01 7 1 29
62 K306-01 888P 1 29
This is the output table:
table2
id element_id element_value lx_seq line_num
59 A101-01 Test 1 25
59 A101-01 Test 1 26
59 K301-01 Test 1 27
59 K301-01 2 28
59 K302-01 18105678982800 2 28
59 K304-01 TOPS 2 28
59 K305-01 7 2 28
59 K306-01 888P 2 28
60 K301-01 Test 1 27
60 K301-01 2 28
60 K302-01 18105678982800 2 28
60 K304-01 TOPS 2 28
60 K305-01 7 2 28
60 K306-01 888P 2 28
61 K301-01 Test 1 27
61 K301-01 1 27
62 K301-01 Test 1 27
62 K301-01 2 28
62 K301-01 18105678982800 3 29
62 K304-01 TOPS 3 29
62 K305-01 7 3 29
62 K306-01 888P 3 29
I need to update lx_seq if and only if in 1 id, we have multiple K3 + line_num combination. It should not work for any other elements like A1.
How can I increase lx_seq based on the above condition in Oracle SQL?

you need dense_rank() function for lx_seq column as below
dense_rank() over (partition by id order by line_num) as lx_seq,
and it's not to hold an already calculated column within a column, that violates the normalization rule for database.

You could do something like following using dense_rank
--first select all not K3 rows and keep lx_seq same
SELECT id, element_id, element_value, lx_seq, line_num
from table1 where element_id not like 'K3%'
UNION ALL
--select K3 rows and based on id and line_num generate rank
SELECT id, element_id, element_value,
dense_rank() over (partition by id, line_num order by line_num) lx_seq,
line_num
from table1 where element_id like 'K3%'
Example Update statement:
MERGE INTO table1 A USING
(
SELECT id, element_id, element_value,
dense_rank() over (partition by id, line_num order by line_num) lx_seq,
line_num
FROM table1
WHERE element_id LIKE 'K3%'
) B
ON (A.id = B.id AND A.line_num = B.line_num AND A.element_id = B.element_id)
WHEN MATCHED THEN
UPDATE SET A.lx_seq = B.lx_seq;

Related

Foreign key value count for each row in a table

I'm trying to generate a table which returns a count for the preceding instances of the foreign key value up to and including the foreign key value on that row.
ForeignIDValue is non-nullable.
I've tried table variables and common table expressions but it gets long winded and messy.
Is there a more elegant and concise way of doing it?
So table A
PrimaryKeyValue ForeignIDValue ProgressiveForeignIDValueCount
15 42 NULL
16 42 NULL
17 43 NULL
18 42 NULL
19 42 NULL
20 42 NULL
24 42 NULL
26 42 NULL
27 42 NULL
29 42 NULL
30 42 NULL
31 42 NULL
32 42 NULL
35 42 NULL
36 42 NULL
37 42 NULL
38 42 NULL
39 42 NULL
40 44 NULL
41 45 NULL
42 46 NULL
43 45 NULL
Needs to become Table B
PrimaryKeyValue ForeignIDValue ProgressiveForeignIDValueCount
15 42 1
16 42 2
17 43 1
18 42 3
19 42 4
20 42 5
24 42 6
26 42 7
27 42 8
29 42 9
30 42 10
31 42 11
32 42 12
35 42 13
36 42 14
37 42 15
38 42 16
39 42 17
40 44 1
41 45 1
42 46 1
43 45 2
SELECT PrimaryKeyValue, ForeignIDValue,
ProgressiveForeignIDValueCount = ROW_NUMBER() OVER
(PARTITION BY ForeignIDValue ORDER BY PrimaryKeyValue)
FROM dbo.[your table name]
ORDER BY PrimaryKeyValue, ProgressiveForeignIDValueCount;
Example db<>fiddle

How to create dynamic row in sql without inserting a value?

I have a requirement to add dynamic rows based on results fetched by SQL query. I've written a query that shows result something like the below:
Value
Name
1
Test 1
2
Test 2
.
.
n
n
The above SQL result will return a dynamic number of rows. (Number of rows not fixed)
So I want to add a column with values like Parent1, Parent2, and so on based on the number of rows. Suppose my query returns a total of 300 rows then the first row should be named as Parent1 in column Value and In name both, Then result of my query until the 150th row then another dynamic row with value column as Parent2 field and so on like below table.
Value
Name
Parent1
Parent 1
1
Test 1
2
Test 2
.
.
Parent2
Parent2
151
Test 151
.
.
n
n
Please Note : I can not use DDL or DML Commands to achive this.
Suppose this is your original query
select
to_char(rownum) value, 'Test '||rownum name
from dual
connect by level <= 6
;
VALUE NAME
---------- ----------
1 Test 1
2 Test 2
3 Test 3
4 Test 4
5 Test 5
6 Test 6
and you want to introdues two header Parent lines.
You may use NTILEto split the original query in two parts ordering on some column (here VALUE)
NTILE(2) OVER (ORDER BY VALUE) nt
Change the number in NTILE to increase the split.
The query below uses the original query as base, calculates the NTILE for the split, adds with UNION ALL the Parent rows.
Most importantly covers the correct order using the NTILE number (nt), the source (first parent row than data) and the value.
with dt as ( /* your original query */
select
to_char(rownum) value, 'Test '||rownum name
from dual
connect by level <= 6
)
select VALUE, NAME,
NTILE(2) OVER (ORDER BY VALUE) nt, /* modify to change split */
1 src
from dt
union all
select
'Parent'||rownum value,
'Parent'||rownum name,
rownum nt, 0 src
from dual connect by level <= 2 /* modify to change split */
order by nt, src, value;
VALUE NAME NT SRC
---------------------------------------------- ---------------------------------------------- ---------- ----------
Parent1 Parent1 1 0
1 Test 1 1 1
2 Test 2 1 1
3 Test 3 1 1
Parent2 Parent2 2 0
4 Test 4 2 1
5 Test 5 2 1
6 Test 6 2 1
The query below will generate a list of parents/non-parents using CONNECT BY. You can change the 300 to the number of rows you want to generate and you change the 150 in the query to have a parent generated that many number of rows.
SELECT LEVEL,
CASE
WHEN MOD (LEVEL, 150) = 0 OR LEVEL = 1
THEN
'Parent' || TO_CHAR (TRUNC (LEVEL / 150) + 1)
ELSE
TO_CHAR (LEVEL)
END AS VALUE,
CASE
WHEN MOD (LEVEL, 150) = 0 OR LEVEL = 1
THEN
'Parent' || TO_CHAR (TRUNC (LEVEL / 150) + 1)
ELSE
'Test ' || TO_CHAR (LEVEL)
END AS VALUE
FROM DUAL
CONNECT BY LEVEL <= 300;
A similar approach, more dynamic.
col value for 9999
col name for a20
define limit = &1
define split = &2
select level as lvl,
case
when mod (level, &&split) = 0 or level = 1
then
'parent' || to_char (trunc (level / &&split) + 1)
else
to_char (level)
end as name,
case
when mod (level, &&split) = 0 or level = 1
then
'parent' || to_char (trunc (level / &&split) + 1)
else
'test ' || to_char (level)
end as value
from dual
connect by level <= &&limit
/
Executed as script, you inform two parameters, the total number of values and the split value.
SQL> #generate.sql 100 50
old 3: when mod (level, &&split) = 0 or level = 1
new 3: when mod (level, 50) = 0 or level = 1
old 5: 'parent' || to_char (trunc (level / &&split) + 1)
new 5: 'parent' || to_char (trunc (level / 50) + 1)
old 10: when mod (level, &&split) = 0 or level = 1
new 10: when mod (level, 50) = 0 or level = 1
old 12: 'parent' || to_char (trunc (level / &&split) + 1)
new 12: 'parent' || to_char (trunc (level / 50) + 1)
old 17: connect by level <= &&limit
new 17: connect by level <= 100
LVL NAME VALUE
---------- -------------------- ----------------------------------------------
1 parent1 parent1
2 2 test 2
3 3 test 3
4 4 test 4
5 5 test 5
6 6 test 6
7 7 test 7
8 8 test 8
9 9 test 9
10 10 test 10
11 11 test 11
LVL NAME VALUE
---------- -------------------- ----------------------------------------------
12 12 test 12
13 13 test 13
14 14 test 14
15 15 test 15
16 16 test 16
17 17 test 17
18 18 test 18
19 19 test 19
20 20 test 20
21 21 test 21
22 22 test 22
LVL NAME VALUE
---------- -------------------- ----------------------------------------------
23 23 test 23
24 24 test 24
25 25 test 25
26 26 test 26
27 27 test 27
28 28 test 28
29 29 test 29
30 30 test 30
31 31 test 31
32 32 test 32
33 33 test 33
LVL NAME VALUE
---------- -------------------- ----------------------------------------------
34 34 test 34
35 35 test 35
36 36 test 36
37 37 test 37
38 38 test 38
39 39 test 39
40 40 test 40
41 41 test 41
42 42 test 42
43 43 test 43
44 44 test 44
LVL NAME VALUE
---------- -------------------- ----------------------------------------------
45 45 test 45
46 46 test 46
47 47 test 47
48 48 test 48
49 49 test 49
50 parent2 parent2
51 51 test 51
52 52 test 52
53 53 test 53
54 54 test 54
55 55 test 55
LVL NAME VALUE
---------- -------------------- ----------------------------------------------
56 56 test 56
57 57 test 57
58 58 test 58
59 59 test 59
60 60 test 60
61 61 test 61
62 62 test 62
63 63 test 63
64 64 test 64
65 65 test 65
66 66 test 66
LVL NAME VALUE
---------- -------------------- ----------------------------------------------
67 67 test 67
68 68 test 68
69 69 test 69
70 70 test 70
71 71 test 71
72 72 test 72
73 73 test 73
74 74 test 74
75 75 test 75
76 76 test 76
77 77 test 77
LVL NAME VALUE
---------- -------------------- ----------------------------------------------
78 78 test 78
79 79 test 79
80 80 test 80
81 81 test 81
82 82 test 82
83 83 test 83
84 84 test 84
85 85 test 85
86 86 test 86
87 87 test 87
88 88 test 88
LVL NAME VALUE
---------- -------------------- ----------------------------------------------
89 89 test 89
90 90 test 90
91 91 test 91
92 92 test 92
93 93 test 93
94 94 test 94
95 95 test 95
96 96 test 96
97 97 test 97
98 98 test 98
99 99 test 99
LVL NAME VALUE
---------- -------------------- ----------------------------------------------
100 parent3 parent3
100 rows selected.

Rank() with Null first in Bigquery based on multiple columns

I have a data like as shown below
Subject_id T1 T2 T3 T4 T5
1234
1234 21 22 23 24 25
3456 34 31
3456 34 31 36 37 39
5678 65 64 62 61 67
5678 65 64 62 67
9876 12 13 14 15 16
4790 47 87 52 13 16
As you can see above, subject_ids 1234,3456 and 5678 are repeating.
I would like to remove those repeating subjects when they have null/empty/blank value in any of the columns like T1,T2,T3,T4,T5.
Now the problem is in real time, I have more than 250 columns and not sure whether I can put 250 where clause checking for null value. So, I was trying with row_number(), rank(). Not sure which one is better. The below is what I was trying
SELECT *,ROW_NUMBER() OVER(PARTITION BY subject_id,T1,T2,T3,T4,T5) NULLS FIRST
from table A;
But it throws syntax error Syntax error: Unexpected keyword NULLS at [1:62]
I expect my output to be like below
Subject_id T1 T2 T3 T4 T5
1234 21 22 23 24 25
3456 34 31 36 37 39
5678 65 64 62 61 67
9876 12 13 14 15 16
4790 47 87 52 13 16
As you can see, the output doesn't contain rows which had at least 1 null/empty/blank value in T1,T2,T3,T4,T5 columns.
Can help please?
Below is for BigQuery Standard SQL
#standardSQL
SELECT *
FROM `project.dataset.table` t
WHERE NOT REGEXP_CONTAINS(FORMAT('%t', t), r'NULL')
If to apply to sample data from your question - output is
Row Subject_id t1 t2 t3 t4 t5
1 1234 21 22 23 24 25
2 3456 34 31 36 37 39
3 5678 65 64 62 61 67
4 9876 12 13 14 15 16
5 4790 47 87 52 13 16
I think you want:
SELECT *,
ROW_NUMBER() OVER (PARTITION BY subject_id
ORDER BY (T1 IS NULL OR T2 IS NULL OR T3 IS NULL OR T4 IS NULL OR T5 IS NULL) DESC
)
FROM table A;
I might approach this problem differently, but this appears to be what you are trying to write.

How to divide a result set into equal parts?

I have a table new_table
ID PROC_ID DEP_ID OLD_STAFF NEW_STAFF
1 15 43 58 ?
2 19 43 58 ?
3 29 43 58 ?
4 31 43 58 ?
5 35 43 58 ?
6 37 43 58 ?
7 38 43 58 ?
8 39 43 58 ?
9 58 43 58 ?
10 79 43 58 ?
How I can select all proc_ids and update new_staff, for example
ID PROC_ID DEP_ID OLD_STAFF NEW_STAFF
1 15 43 58 15
2 19 43 58 15
3 29 43 58 15
4 31 43 58 15
5 35 43 58 23
6 37 43 58 23
7 38 43 58 23
8 39 43 58 28
9 58 43 58 28
10 79 43 58 28
15 - 4(proc_id)
23 - 3(proc_id)
28 - 3(proc_id)
58 - is busi
where 15, 23, 28 and 58 staffs in one dep
"how to divide equal parts"
Oracle has a function, ntile() which splits a result set into equal buckets. For instance this query puts your posted data into four buckets:
SQL> select id
2 , proc_id
3 , ntile(4) over (order by id asc) as gen_staff
4 from new_table;
ID PROC_ID GEN_STAFF
---------- ---------- ----------
1 15 1
2 19 1
3 29 1
4 31 2
5 35 2
6 37 2
7 38 3
8 39 3
9 58 4
10 79 4
10 rows selected.
SQL>
This isn't quite the solution you want but you need to clarify your requirements before it's possible to provide a complete answer.
update new_table
set new_staff='15'
where ID in('1','2','3','4')
update new_table
set new_staff='28'
where ID in('8','9','10')
update new_table
set new_staff='23'
where ID in('5','6','7')
Not sure if this is what you mean.

trying to get user assign to under another user

I am trying to write a sql query in which I want all the list of uid assigned under another uid from the below given table
uid rid assTo
1 1 NULL
2 2 1
3 1 2
6 11 3
7 11 1
17 11 1
18 11 1
19 11 1
21 11 1
22 2 1
23 11 22
24 2 22
25 10 24
26 10 24
27 11 26
28 11 26
29 10 24
30 11 3
31 11 29
32 11 29
33 11 29
34 11 29
35 11 29
36 11 29
37 11 29
38 11 29
39 11 29
40 11 29
41 11 29
47 11 2
48 11 2
50 11 26
51 11 2
52 11 26
53 11 29
55 11 1
56 11 1
57 11 652
68 11 652
70 11 652
71 11 652
72 11 2
74 1 1
75 11 2
76 11 652
80 11 652
86 11 652
87 11 1
88 11 26
89 11 29
I want all the list of uid assigned to another uid i.e. designated in assto column in above table
How can I implement it?
My query
select u1.assignto 'assignto',u2.userid 'userid'
from users u1 join users u2
on u1.assignto=u2.userid
My desired output is that when i search uid = 1
i should the list of the uid available in the above table.
IN solution:
select * from tablename
where uid in (select assTo from tablename)
Self JOIN solution:
select distinct t1.*
from tablename t1
JOIN tablename t2 ON t1.uid= t2.assTo
EXISTS solution (similar to IN):
select *
from tablename t1
where exists (select 1 from tablename t2
where t2.assTo = t1.uid)