Jumbling the data within the table - sql

I have a scenario, where i have to mask the data with data within the table
let's say I have a table student_details(ID, CODE, NAME)
1 A XYZ
2 A 123
3 A QWERTY
I want the output as
1 A QWERTY
2 A XYZ
3 A 123
I want the name to be within the name list in that table
for same id I Want different name which is in the table.
select * from emp_details order by dbms_random.value;
is giving some random names which are not in list.
Can any one help me with this?

Here's one option: recalculate the ID value using ROW_NUMBER analytic function which orders rows by the hash value over concatenated name, code and id columns (that's just for example; you can pick something different).
SQL> with test (id, code, name) as
2 (select 1, 'A', 'XYZ' from dual union all
3 select 2, 'A', '123' from dual union all
4 select 3, 'A', 'QUERTY' from dual
5 ),
6 inter as
7 (select row_number() over (order by ora_hash(name || code || id)) id,
8 code, name
9 from test
10 )
11 select t.id, t.code, i.name
12 from test t join inter i on t.id = i.id;
ID C NAME
---------- - ------
1 A XYZ
2 A QUERTY
3 A 123
SQL>

If you intend to permutate selected columns in your table and leave the rest of the table unchanged, you may use a join with a key permutation table.
Assume your data as follows:
ID CODE NAME
---------- ---- ------
1001 A XYZ
1002 B 123
1004 C QUERTY
1005 A FOO
Note, that the PK is not continuous, wich is the generall case. If you have the PK a continuous sequence starting with 1, you may even simplify the solution (as proposed in other answer).
First lets define the permutation table assigning to each PK a new key in random order.
create table PERM as
with rn as (
select
id,
row_number() over (order by id) rn,
row_number() over (order by dbms_random.value) rn_new
from student)
select a.ID, b.ID ID_NEW
from rn a
join rn b
on a.RN = b.RN_NEW;
ID ID_NEW
---------- ----------
1001 1004
1002 1001
1004 1005
1005 1002
The query defines two row_number sequences, first in the order of the PK, second in random order. The final join gets the original and new (permutated) IDs.
Now to permute a selected colums is as easy as to join your table twice with the permutation table in between and choose preserved columns from the first table, the permuted columns from the second one.
select a.ID, a.code, b.name
from student a
join PERM p on a.id = p.id
join student b on p.id_new = b.id
order by a.id;
ID CODE NAME
---------- ---- ------
1001 A QUERTY
1002 B XYZ
1004 C FOO
1005 A 123
As far as you preserv the permutation table you can reconstruct the former state, if you drop it, there is no way to get the original data.

Related

Oracle SQL: Duplicate rows when trying to query data

Table A:
Plant ID
Plant Name
Unit Name
Technology Type
1
ABC
Unit 1
SMR
1
ABC
Unit 2
Electrolysis
Table B:
Plant ID
Feedstock Type
1
Natural Gas
1
Water
select
a.PLANT_NAME,
a.UNIT_NAME,
b.FEEDSTOCK_TYPE,
a.TECHNOLOGY_TYPE
from
Table A a
inner join Table B b
on a.plant_ID = b.plant_ID
When I run the code, the result will be
Plant Name
Unit Name
Technology Type
Feedstock
ABC
Unit 1
SMR
Natural Gas
ABC
Unit 1
SMR
Water
ABC
Unit 2
Electrolysis
Natural Gas
ABC
Unit 2
Electrolysis
Water
But I expect the result to be
Plant Name
Unit Name
Technology Type
Feedstock Type
ABC
Unit 1
SMR
Natural Gas
ABC
Unit 2
Electrolysis
Water
The problem is I dont have a common primary key to link each unit to each feedstock. I only have one common plant id between the 2 tables. Any thoughts? Thanks in advance! :)
You have nothing to join on beyond the plant_id. If you want to "align" the rows, you can introduce a new column using row_number():
select a.PLANT_NAME, a.UNIT_NAME, b.FEEDSTOCK_TYPE, a.TECHNOLOGY_TYPE
from (select a.*,
row_number() over (partition by plant_id order by unit_name) as seqnum
from table_A a
) a join
(select b.*,
row_number() over (partition by plant_id order by feedstock_type) as seqnum
from table_B b
) b
on a.plant_ID = b.plant_ID and a.seqnum = b.seqnum;
You can't get it as there's no way to distinguish whether to take "Natural Gas" or "Water" as both values share the same PLANT_ID in table_B.
You can take one of them using an aggregate function, e.g. MAX, but that's just a workaround which doesn't return what you wanted anyway. For example (sample data in lines #1 - 9) (note MAX in line #12 which also requires the GROUP BY clause):
SQL> with
2 table_a (plantid, plant_name, unit_name, technology_type) as
3 (select 1, 'ABC', 'Unit 1', 'SMR' from dual union all
4 select 1, 'ABC', 'Unit 2', 'Electrolysis' from dual
5 ),
6 table_b (plantid, feedstock_type) as
7 (select 1, 'Natural Gas' from dual union all
8 select 1, 'Water' from dual
9 )
10 select a.plant_name,
11 a.unit_name,
12 max(b.feedstock_type) feedstock_type,
13 a.technology_type
14 from table_a a join table_b b on a.plantid = b.plantid
15 group by a.plant_name, a.unit_name, a.technology_type;
PLA UNIT_NAME FEEDSTOCK_TYPE TECHNOLOGY_TYPE
--- ---------- --------------- ---------------
ABC Unit 2 Water Electrolysis
ABC Unit 1 Water SMR
SQL>
There are no implicit values in a table, only explicit ones in columns. You appear to want to join "row 1" of table A to "row 1" of table B, and so on. In that case you need an explicit value 1, 2, ... in column in each table and join on that.

find all rows after the recent update using oracle

I tried below query to bring all rows after last Action="UNLOCKED", but ORDER BY is not allowed in subquery it seems.
SELECT *
FROM TABLE
WHERE id >= (SELECT MAX(id)
FROM TABLE
WHERE ACTION='UNLOCKED' AND action_id=123
ORDER BY CREATE_DATE DESC);
Sample data
Id action_id Action ... CREATE_DATE
1 123 ADD 03/18/2018
2 123 Unlocked 03/19/2018
3 123 Updated1 03/19/2018
4 123 Updated2 03/19/2018
5 123 Unlocked 03/20/2018
6 123 Updated3 03/20/2018
7 123 Updated4 03/20/2018
Output should be rows with id 5,6,7. What should i use to get this output
you could use an inner join on subselect for max create_date
select * from TABLE
INNER JOIN (
select max(CREATE_DATE) max_date
from TABLE
where Action = 'Unlocked' ) T on t.max_date = TABLE.CREATE_DATE
You need not order the inner query because it will return only one value. You can do it as follows
SELECT * FROM TABLE WHERE id >= (select max(id) from TABLE where ACTION='UNLOCKED' and action_id=123);

Find next row with specific value in a given row

The table I have now looks something like this. Each row has a time value (on which the table is sorted in ascending order), and two values which can be replicated across rows:
Key TimeCall R_ID S_ID
-------------------------------------------
1 100 40 A
2 101 50 B
3 102 40 C
4 103 50 D
5 104 60 A
6 105 40 B
I would like to return something like this, wherein for each row, a JOIN is applied such that the S_ID and Time_Call of the next row that shares that row's R_ID is displayed (or is NULL if that row is the last instance of a given R_ID). Example:
Key TimeCall R_ID S_ID NextTimeCall NextS_ID
----------------------------------------------------------------------
1 100 40 A 102 C
2 101 50 B 103 D
3 102 40 C 105 B
4 103 50 D NULL NULL
5 104 60 A NULL NULL
6 105 40 B NULL NULL
Any advice on how to do this would be much appreciated. Right now I'm joining the table on itself and staggering the key on which I'm joining, but I know this won't work for the instance that I've outlined above:
SELECT TOP 10 Table.*, Table2.TimeCall AS NextTimeCall, Table2.S_ID AS NextS_ID
FROM tempdb..#Table AS Table
INNER JOIN tempdb..#Table AS Table2
ON Table.TimeCall + 1 = Table2.TimeCall
So if anyone could show me how to do this such that it can call rows that aren't just consecutive, much obliged!
Use LEAD() function:
SELECT *
, LEAD(TimeCall) OVER (PARTITiON BY R_ID ORDER BY [Key]) AS NextTimeCall
, LEAD(S_ID) OVER (PARTITiON BY R_ID ORDER BY [Key]) AS NextS_ID
FROM Table2
ORDER BY [Key]
SQLFiddle DEMO
This is only test example I had close by ... but i think it could help you out, just adapt it to your case, it uses Lag and Lead ... and it's for SQL Server
if object_id('tempdb..#Test') IS NOT NULL drop table #Test
create table #Test (id int, value int)
insert into #Test (id, value)
values
(1, 1),
(1, 2),
(1, 3)
select id,
value,
lag(value, 1, 0) over (order by id) as [PreviusValue],
lead(Value, 1, 0) over (order by id) as [NextValue]
from #Test
Results are
id value PreviusValue NextValue
1 1 0 2
1 2 1 3
1 3 2 0
Use an OUTER APPLY to select the top 1 value that has the same R_ID as the first Query and has a higher Key field
Just change the TableName to the actual name of your table in both parts of the query
SELECT a.*, b.TimeCall as NextTimeCall, b.S_ID as NextS_ID FROM
(
SELECT * FROM TableName as a
) as a
OUTER APPLY
(
SELECT TOP 1 FROM TableName as b
WHERE a.R_ID = b.R_ID
AND a.Key > B.Key
ORDER BY Key ASC
) as b
Hope this helps! :)
For older versions, here is one trick using Outer Apply
SELECT a.*,
nexttimecall,
nexts_id
FROM table1 a
OUTER apply (SELECT TOP 1 timecall,s_id
FROM table1 b
WHERE a.r_id = b.r_id
AND a.[key] < b.[key]
ORDER BY [key] ASC) oa (nexttimecall, nexts_id)
LIVE DEMO
Note : It is better to avoid reserved keywords(Key) as column/table names.

Using table joins to put seq no to the grouped items in the select SQL Query result

can any one help to solve this issue in SQL SERVER 2008. I want to put seq numbering to the grouped items in the select query result.
select a.grade, a.name, a.age, seqno
from tab A join tab B on A.id = B.id
[group based on Grade column]
The result should show as shown below.
SeqNo Grade Name Age
----- ----- ---- ---
1 A abc 23
2 A xyz 7
1 B dfg 34
2 B sxd 23
3 B vvv 56
In the below query, how i can join the second table and using a "join'
SELECT ROW_NUMBER() OVER ( PARTITION BY [a.Grade] ORDER BY [a.Name]) AS 'SeqNo',
[a.Grade], [a.Name], [a.Age]
FROM Employee a WITH (NOLOCK)
ORDER BY [a.Grade],[a.Name]

Select Distinct value from column and return all rows

I'm trying to select distinct value from a column but return all rows related to the values selected. In psuedo code it will look like this.
SELECT *
FROM table
WHERE field is Distinct
I googled the question and I've tried using GROUP BY but the query never executes. Thanks for the help.
I am using a Microsoft SQL Database.
The Data looks like this:
CodeId Code CatalogType CodeGroup CodeText CodeGroupText CodeDesc State_ID
------- ----- ------------- ---------- -------- -------------- --------- ---------
1 AAAA 1 100 Plastic Plastic Center NULL 2
2 BBBB 1 100 Glass Glass Center NULL 2
3 CCCC 1 101 Steel Steel Center NULL 2
I just want to the data to look the same just where the code group is distinct.
Data would look like this:
CodeId Code CatalogType CodeGroup CodeText CodeGroupText CodeDesc State_ID
------- ----- ------------- ---------- -------- -------------- --------- ---------
1 AAAA 1 100 Plastic Plastic Center NULL 2
3 CCCC 1 101 Steel Steel Center NULL 2
You could always use a subquery to return the min(codeid) for each codegroup and join this result to your table:
select t1.codeid,
t1.code,
t1.catalogtype,
t1.codegroup,
t1.codetext,
t1.codegrouptext,
t1.codedesc,
t1.state_id
from yourtable t1
inner join
(
select MIN(codeid) codeid, codegroup
from yourtable
group by codegroup
) t2
on t1.codeid = t2.codeid
and t1.codegroup = t2.codegroup
In most databases, you can do:
select t.*
from (select t.*
row_number() over (partition by field order by field) as seqnum
from t
) t
where seqnum = 1
SELECT field1,field2,max(field3),sum(field4)
FROM table
GROUP BY field1, field2
This will give you all distinct field1 and field2's. You cannot get the field3 field directly (with this grouping), since there may be multiple field3's.
if you need just distinct values from a column, i.e. to find a set of unique values in one column, then this code helps you (for sql server at least):
select distinct
columnName
from tableT