SQL: Substitute values between columns - sql

I have a (very simplified) (Oracle) table like this. The row number are only for better understanding, they have no deeper meaning.
Row
value
1
Jim
2
John
3
Mike
Now I find the "error" - the names are messed up. All "Jims" should be renamed to "John", John changes to Mike and Mike changes to Jim.
My first update changes Jim to John.
My second update should change John to Mike.
But the real problem is: Now I have two Johns - the old and the renamed Jim. How to find and modify the "old John" without changing the other?
My idea is to use temp-values:
Jim to temp_John
John to temp_mike
Mike to Jim
temp_John to John
temp_mike to Mike
Good idea? Or are there better ones? Or is it the totally wrong way?
Edit:
I reality, there are not three names, but 700 - 1000 numbers, so
25 to 345
558 to 2
1 to 38
...

Nah, it's just a single UPDATE with a CASE expression:
SQL> select * From test order by crow;
CROW VALU
---------- ----
1 Jim
2 John
3 Mike
SQL> update test set
2 value = case when value = 'Jim' then 'John'
3 when value = 'John' then 'Mike'
4 when value = 'Mike' then 'Jim'
5 end;
3 rows updated.
SQL> select * From test order by crow;
CROW VALU
---------- ----
1 John
2 Mike
3 Jim
SQL>

You can use decode:
update test set value = decode(value,'JIM','JOHN','JOHN','MIKE','MIKE','JIM');
db<>fiddle here
For numbers you can use negative numbers as a temporarity:
update test value = decode(value, 65537, -7777776777777, value);
update test value = decode(value, 7777776777777, -45, value);
update test value = decode(value, 45, -65537, value);
update test set value = abs(value);
This way you can have as many numbers as you want as it uses one query per number.
updated db<>fiddle here for numbers example

You should create a table with two columns:
create table value_pairs
(old_value integer,
new_value integer,
primary key (old_value));
After creating the table you have to insert the old and new values:
insert into value_pairs values (1, 2);
insert into value_pairs values (2, 3);
etc.
Finally run this SQL statement:
update your_table t
set value =
(select new_value
from value_pairs p
where p.old_value = t.value);
In this case you don't have to write long case or decode statements.

Related

How to specify a limit on Postgres json_agg

I want a JSON output having the distinct values of a column and also a limited number of rows.
This is the sample table that I have in a Postgres Database:
Name Author Copies Sold
---- ------- -----------
Book1 James 10
Book2 James 10
Book3 Alex 12
Book4 James 11
Book5 Amanda 1
I want to write an SQL query that returns a list of all the unique author names and also every row but with a limit of 3
This is the SQL query that I have so far
WITH first_query AS(
SELECT * FROM sample_table LIMIT 3
)
SELECT json_build_object("all_authors",json_agg(DISTINCT(author)),
"book_details",json_agg(row_to_json(first_query))
)
FROM first_query;
This gives me the following output:
{"all_authors":["James","Alex"],
"book_details":[{"name":"Book1","author":"James","copies sold":10},
{"name":"Book2","author":"James","copies sold":10},
{"name":"Book3","author":"Alex","copies sold":12}]}
In the above output, the only Authors in the list are James and Alex. However, I want the names of all three authors but still limiting "book_details" to the first three. i.e. I want Amanda to be on the list too.
Basically, this is the output I want:
{"all_authors":["James","Alex", "Amanda"],
"book_details":[{"name":"Book1","author":"James","copies sold":10},
{"name":"Book2","author":"James","copies sold":10},
{"name":"Book3","author":"Alex","copies sold":12}]}
How do I get all distinct values of a column and still have a limit on the query?
here is how you can do it;
with cte as (
SELECT * FROM books limit 3
)
SELECT json_build_object('all_authors',json_agg(DISTINCT(author)),'book_details',(select json_agg(row_to_json(cte.*,true)) from cte))
FROM books

Can you use an 'In' operator in a column expression?

Using Oracle 11. Trying to write some simple code for a conceptual demo. To illustrate what I'm trying to achieve, imagine I have SOMETABLE that has two columns: ID and NAME, like so:
ID NAME
---------
1 Tom
2 Larry
3 David
4 Steve
I'm trying to compute a third column that is true if the second column matches one of two hard-coded values. Something like this (which of course doesn't work.)
Select ID,
NAME,
(NAME in ('Larry', 'David')) as IS_FAVORITE
from SOMETABLE
and hoping to get this output...
ID NAME IS_FAVORITE
----------------------
1 Tom FALSE
2 Larry True
3 David True
4 Steve FALSE
Much to my surprise, I'm being told Oracle doesn't have the concept of booleans and I should be using 'numeric strings' or something like that, so this too is fine...
ID NAME IS_FAVORITE
----------------------
1 Tom 'N'
2 Larry 'Y'
3 David 'Y'
4 Steve 'N'
So can you use the IN operator in a column expression like this? If not, how would one compute the column that I am after?
You can achieve your expected output using case expression.
Select ID,
NAME,
CASE
WHEN
NAME in ('Larry', 'David')
THEN
'TRUE'
ELSE
'FALSE'
END as IS_FAVORITE
from SOMETABLE

How to add value to column in which there is already some value

How to add value to column in which there is already some value. So that the value in this console will have maximum 10 lengths.
On start the value in the column have different lengths
Example. I have DB with 2 columns.
Each value in the ID column should have max 10 characters, if they are to be omitted, they should be preceded by zeros
USER ID
1111 0000012345
2222 001234
3333 567890
4444 67890
5555 7778889
I think you want lpad():
update t
set id = lpad(id, 10, '0');
Or you might want this in a select:
select t.*, lpad(id, 10, '0') as id_10
from t;
Your question is not clear.
As per what I understand is, if you want to select ID column which should display length of 10 then below the query.
SELECT RIGHT(REPLICAE('0',10) + ID ,10) FROM <YourTableName>
You can do this like below.
create temp table my_table(
USER1 int,
ID varchar(10));
insert into my_table
values(1111,'0000012345'),
(2222, '001234'),
(3333,'567890'),
(4444,'67890'),
(5555,'7778889');
select USER1,
CASE
WHEN length(ID)<10 THEN CONCAT(repeat('0',10-length(ID)),ID)
ELSE ID
END as ID
from my_table
Result will be like this.
USER1 ID
1111 0000012345
2222 0000999932
3333 0000333413
2222 0000994554
Also in my table I already put constraint that the ID column should be length 10 but if you have values with more than length 10 then I am not sure how you will want to go with that problem as there are different options for that. You can make the whole column "invalid" or truncate it.

Multiply every entry in column by a fixed number in SQL

In SQL, say I have a table of the following layout:
id name age
1 george 12
2 tom 14
3 charles 19
4 henry 21
5 fiona 12
6 kate 14
...
If I discover that I've made a terrible inputting mistake, and that everybody is actually twice their age, is there a way to multiply the age column by 2 in one sweep instead of needing to painstakingly go through the whole column and edit each age individually (pretend I have 500 entries so manual work is out of the question).
Is there a SQL solution?
It's really easy:
UPDATE table SET age=age*2
Yes, run an update:
update theTable set age = age * 2
Depending on the settings in the database, you might not be allowed to run an update without a where (to protect against mistakes), then you would add a dummy comparison:
update theTable set age = age * 2 where 1 = 1

SQL update records with incrementing value starting from 1 each time

I am adding batches of records to a table using a single insert statement. I want each new batch to be allocated incrementing numbers, but starting from 1 each time.
So, if I have
Batch Name IncementingValue
1 Joe 1
1 Pete 2
1 Andy 3
2 Sue 1
2 Mike 2
2 Steve 3
and I then add two records (using a single insert statement) :
3 Dave
3 Paul
How can I run an update statement against this table so that Dave will be set to 1 and Paul to 2. I don't want to use a cursor.
The ranking function ROW_NUMBER should do what you need. You didn't mention any specific rules about how the sequence number should be allocated, so I've done it here using the name:
INSERT targetTable(Batch,Name,IncementingValue)
SELECT BatchId,
Name,
ROW_NUMBER() OVER (ORDER BY Name)
FROM sourceTable
I needed to accomplish something similar with dates and formatted numbers.
Hopefully, someone will find this example useful.
update TEST_TABLE
set ref = reference
from (
select
*,
(CONVERT(VARCHAR(10),GETDATE(),12) + RIGHT('0000' + CAST(ROW_NUMBER() OVER (ORDER BY id) AS VARCHAR(4)), 4)) as reference
from TEST_TABLE
WHERE
test_table.id > 4
and
test_table.id < 8
) TEST_TABLE