How to write conditional select insert statement to fetch record from a table, and based on some particular column's values - sql

for eg:
Table1:
c1,c2,c3 (1,10,'123')
Table2
c1,c2,c3,c4
Now I want to select record from table1 and if c3 column value starting two digit is 12 then I have to populate AA, and if 34 then BB in Table2 c4 column:
Table1 -- (1,10,'123')
Table2 -- (1,10,'123','AA')
Table1 -- (1,10,'3444')
Table2 -- (1,10,'3444','BB')

OP has provided additional information in Comments under the original post. In particular, c3 is VARCHAR2 and always begins with either 12 or 34.
The following should work. If c3 does not begin with 12 or 34, then the value inserted in column c4 will be NULL. That is the default behavior of CASE expressions, so it doesn't need to be coded explicitly; if instead something like 'ZZ' is desired in that case, then we can add an else clause to CASE. (The OP said c3 always begins with either 12 or 34; if so, this is a moot point.)
insert into table2 ( c1, c2, c3, c4 )
select c1, c2, c3,
case when c3 like '12%' then 'AA'
when c3 like '34%' then 'BB'
end
from table1
;
Demo:
SQL> create table table1 ( c1 number, c2 number, c3 varchar2(25) );
Table created.
SQL> insert into table1 ( c1, c2, c3 ) values ( 1, 10, '123' );
1 row created.
SQL> insert into table1 ( c1, c2, c3 ) values ( 1, 10, '3444' );
1 row created.
SQL> commit;
Commit complete.
SQL> select * from table1;
C1 C2 C3
---------- ---------- -------------------------
1 10 123
1 10 3444
2 rows selected.
Then:
SQL> create table table2 ( c1 number, c2 number, c3 varchar2(25), c4 varchar2(10) );
Table created.
SQL> select * from table2;
no rows selected
SQL> insert into table2 ( c1, c2, c3, c4 )
2 select c1, c2, c3,
3 case when c3 like '12%' then 'AA'
4 when c3 like '34%' then 'BB'
5 end
6 from table1
7 ;
2 rows created.
SQL> select * from table2;
C1 C2 C3 C4
---------- ---------- ------------------------- ----------
1 10 123 AA
1 10 3444 BB
2 rows selected.

Related

Oracle Generate a unique sequence value for each group of rows, partitioned by some column

drop table foo;
create table foo (c1 varchar2(10), c2 int);
insert into foo values ('A', 10);
insert into foo values ('A', 11);
insert into foo values ('B', 12);
insert into foo values ('B', 13);
create sequence foo_s;
I would like to group these rows by C1 (A or B), and generate a single sequence value for every row in the same group. For example, the two rows with C1=A should have the same sequence generated number (for example 1024), while the two rows with C1=B would have the same sequence generated number (for example 1025):
I want to do something like the following which of course is invalid syntax:
select c1, c2, foo_s.nextval over (partition by c1) batch_id
from foo
Which outputs the following:
c1 | c2 | batch_id
A | 10 | 1024
A | 11 | 1024
B | 12 | 1025
B | 13 | 1025
I want to do this in a pure SQL Select statement. Failing that I suppose a merge/update is OK. As a last resort I could do it in PL/SQL.
This solution uses a MERGE instead of SELECT, and is failing for me with ORA-02287: sequence number not allowed here
In PostgreSQL, it is possible to use a Sequence this way. For example:
select c1, c2, first_value(batch_id) over (partition by c1)
from (
select c1, c2, nextval('foo_s') batch_id from foo
) foo;
There are several other alternative ways. But it seems like using the sequence in Oracle will always result in ORA-02287: sequence number not allowed here.
You commented that the final result will reside in another table; you didn't post it so I'll assume several things - see if it helps.
This is a source table (you posted):
SQL> select * From foo;
C1 C2
---------- ----------
A 10
A 11
B 12
B 13
This is a target table:
SQL> create table test (c1 varchar2(5), c2 number, batch_id number);
Table created.
Query doesn't require any sequence (as Oracle object); everything is done from existing data. A CTE finds the last batch_id and then adds value generated by the dense_rank analytic function:
SQL> insert into test (c1, c2, batch_id)
2 with max_bid as
3 (select nvl(max(batch_id), 0) max_bid from test)
4 select f.c1,
5 f.c2,
6 dense_rank() over (order by f.c1) + m.max_bid as batch_id
7 from foo f cross join max_bid m;
4 rows created.
Contents of the target table:
SQL> select * from test;
C1 C2 BATCH_ID
----- ---------- ----------
A 10 1
A 11 1
B 12 2
B 13 2
SQL>
Now, as you didn't explain how exactly will you generate next data set, I'll just truncate source table and insert some more rows into it. You'll, perhaps, have some date column (so you'll always take "today's data"), or ... who know, but you?
SQL> truncate table foo;
Table truncated.
SQL> insert all
2 into foo values ('A', 20)
3 into foo values ('A', 21)
4 into foo values ('B', 22)
5 into foo values ('B', 23)
6 select * From dual;
4 rows created.
SQL> select * From foo;
C1 C2
---------- ----------
A 20
A 21
B 22
B 23
SQL>
Re-run the same INSERT statement:
SQL> insert into test (c1, c2, batch_id)
2 with max_bid as
3 (select nvl(max(batch_id), 0) max_bid from test)
4 select f.c1,
5 f.c2,
6 dense_rank() over (order by f.c1) + m.max_bid as batch_id
7 from foo f cross join max_bid m;
4 rows created.
which results in
SQL> select * from test;
C1 C2 BATCH_ID
----- ---------- ----------
A 10 1
A 11 1
B 12 2
B 13 2
A 20 3
A 21 3
B 22 4
B 23 4
8 rows selected.
SQL>
batch_id is incremented, as you wanted.
drop table foo;
create table foo (c1 varchar2(10), c2 int);
insert into foo values ('A', 10);
insert into foo values ('A', 11);
insert into foo values ('B', 12);
insert into foo values ('B', 13);
drop sequence foo_s;
create sequence foo_s;
DROP TABLE temp_tbl;
CREATE TABLE temp_tbl AS
SELECT
d.distinct_c1,
foo_s.nextval as val
FROM
(
SELECT
DISTINCT c1 AS distinct_c1
FROM
foo
)d;
SELECT
foo.c1,
foo.c2,
t.val
FROM
foo
LEFT JOIN
temp_tbl t
ON
t.distinct_c1=foo.c1;
Repeat assignment each query:
SELECT c1, c2, DENSE_RANK() OVER (ORDER BY c1) batch_id
FROM foot
Assign new batch1 values only when there are new C1 values:
MERGE INTO foo2 tgt
USING (WITH data AS (SELECT src.c1,
src.c2,
tgt.batch_id
FROM foo src,
(SELECT c1,MAX(batch_id) batch_id
FROM foo2
GROUP BY c1) tgt
WHERE src.c1 = tgt.c1(+))
SELECT * -- remove this SELECT if you don't need to re-update existing rows for some other reason. they already have the correct batch_id
FROM data
WHERE batch_id IS NOT NULL
UNION ALL
SELECT c1,
c2,
x.last_batch_id + DENSE_RANK() OVER (PARTITION BY c1 ORDER BY c1) batch_id
FROM data,
(SELECT MAX(batch_id) last_batch_id
FROM data) x
WHERE batch_id IS NULL) src
ON (src.c1 = tgt.c1,
src.c2 = tgt.c2)
WHEN MATCHED THEN UPDATE SET tgt.batch_id = src.batch_id
WHEN NOT MATCHED THEN INSERT (c1,c2,batch_id)
VALUES (src.c1,src.c2,src.batch_id)
You may isolate sequence.nextval into a function and use the function in any place. Since 12c it is possible to declare functions within select statement.
create view test_batch
as
with function f_get_seq
return number
as
pragma udf;
begin
return foo_s.nextval;
end;
grp as (
select
c1, f_get_seq() as batch_id
from foo
group by c1
)
select *
from foo
join grp
using(c1)
select *
from test_batch
C1
C2
BATCH_ID
B
12
2
B
13
2
A
10
1
A
11
1
select *
from test_batch
C1
C2
BATCH_ID
A
10
3
A
11
3
B
12
4
B
13
4
fiddle
UPD.
Or, if you want to avoid double access to the foo, the same with match_recognize:
create view test_batch
as
with function f_get_seq
return number
as
pragma udf;
begin
return foo_s.nextval;
end;
select *
from foo
match_recognize (
partition by c1
measures
final first(f_get_seq()) as batch_id
all rows per match
pattern (a+)
define
a as 1=1
)
fiddle
... or with model:
create view test_batch
as
with function f_get_seq
return number
as
pragma udf;
begin
return foo_s.nextval;
end;
select *
from foo
model
partition by (c1)
dimension by (
row_number() over(partition by c1 order by null) as rn
)
measures (
c2, 0 as batch_id
)
rules update (
batch_id[1] = f_get_seq(),
batch_id[rn > 1] = batch_id[1]
)
fiddle

Difference in two tables display difference in rows in single column

I have two tables which return 3 columns (Account #, Amount, Site Number)
Sample:
Table # 1
111111, 200, 14
111111,-200, 14
111111, 400, 15
111111, -400, 15
Table # 2
111111, 201, 14
111111,-200, 14
111111, 400, 15
111111, -400, 15
I am trying to graft a query that will no only show me the differences between the two tables like minus or where not exists but allow me pivot the data that is different from the one table to a column in the return
Something like this:
Act#: TblA Amount TblB Amount Site
111111, 200, 201, 14
When I use minus it simply gives me back the row in the top table that is different so if I did:
select * from TblA
MINUS
select * from TblB
Result:
111111, 200, 14
I know there must be way to do this and any help would be great!
Maybe the following queries will help. Principle: find the differences between the tables (contents of A minus contents of B, and vice versa),
then use these in a join, in order to "collapse" the result set. Using your test data (Oracle 12c):
(
select c1, c2, c3, 'in table#1' location from table#1
minus
select c1, c2, c3, 'in table#1' from table#2
)
union all
(
select c1, c2, c3, 'in table#2 (not in table#1)' from table#2
minus
select c1, c2, c3, 'in table#2 (not in table#1)' from table#1
);
-- result
C1 C2 C3 LOCATION
---------- ---------- ---------- ---------------------------
111111 200 14 in table#1
111111 201 14 in table#2 (not in table#1)
If there are no duplicates for the C1/C3 combinations, the following JOIN will give you the required result. (Maybe this will be "good enough" for your situation ...)
select
A.c1
--, B.c1
, A.c2
, B.c2
, A.c3
--, B.c3
from
(
select * from table#1
minus
select * from table#2
) A join (
select * from table#2
minus
select * from table#1
) B on A.c1 = B.c1 and A.c3 = B.c3
;
-- result
C1 C2 C2 C3
---------- ---------- ---------- ----------
111111 200 201 14
Dbfiddle here.
You could use FULL JOIN:
SELECT *
FROM tabA a
FULL JOIN tabB b
ON a.id = b.id -- here should be PK or UNIQUE col
WHERE NOT EXISTS (SELECT a.Account, a.Amount, a.Site_Number FROM dual
INTERSECT
SELECT b.Account, b.Amount, b.Site_Number FROM dual);
db<>fiddle demo

sql update in teradata

table_1
(
c1 int
,c2 int
,c3 int
);
insert into table_1 values (1,2,3);
update table_1
set c2 = c2 + 5 --c2's value suppose to be 7 now
,c3 = c2 + c1; --c3's value suppose to be 7 + 1 = 8 now
select c1, c2, c3 from table_1;
result: 1, 7, 3;
Result supposed to be: 1, 7, 8.
But c3's value is still 3, no change has been made.
Sorry for my English.
The UPDATE statement uses the values "before" UPDATE, i.e
c2 = c2 + 5 => 2 + 5 = 7
but
c3 = c2 + c1 => 1 + 2 = 3
Executes like this:
SQL>create table table_1 ( c1 int ,c2 int ,c3 int );
SQL>insert into table_1 values (1,2,3);
SQL>update table_1
SQL& set c2 = c2 + 5 --c2's value is set to 7 now
SQL& ,c3 = c2 + c1; --c3's value is set to 2 + 1 = 3 now
1 row updated
SQL>select c1, c2, c3 from table_1;
c1 c2 c3
=========== =========== ===========
1 7 3
1 row found
You can even move values between the columns:
SQL>update table_1 set c3 = c2, c2 = c1, c1 = c3;
1 row updated
SQL>select c1, c2, c3 from table_1;
c1 c2 c3
=========== =========== ===========
3 1 7
1 row found
To update the columns one by one, after each other, use separate UPDATE statements:
SQL>create table table_1 ( c1 int ,c2 int ,c3 int );
SQL>insert into table_1 values (1,2,3);
SQL>update table_1
SQL& set c2 = c2 + 5; -- c2's value suppose to be 7 now
1 row updated
SQL>update table_1
SQL& set c3 = c2 + c1; -- c3's value suppose to be 7 + 1 = 8 now
1 row updated
SQL>select * from table_1;
c1 c2 c3
=========== =========== ===========
1 7 8
1 row found

Select first row in each GROUP BY group

I have a requirement in my project that I have this data with me:
C1 | C2 | C3 | C4
A | B | 2 | X
A | B | 3 | Y
C | D | 4 | Q
C | D | 1 | P
Where C1, C2, C3 and C4 are columns name in Database
And I have need to show data like this
C1 | C2 | C3 | C4
A | B | 5 | X
C | D | 5 | Q
The answer to this is fairly simple. Just follow my solution below:
--CREATE THE SAMPLE TABLE
CREATE TABLE TABLE1 (C1 char(1) NULL, C2 char(1) NULL, C3 int NULL, C4 char(1) NULL);
GO
--INSERT THE SAMPLE VALUES
INSERT INTO TABLE1 VALUES ('A', 'B', 2, 'X'), ('A', 'B', 3, 'Y'), ('C', 'D', 4, 'Q'), ('C','D', 1, 'P');
GO
--SELECT SUM(C3) AND GROUP BY ONLY C1 AND C2, THEN SELECT TOP 1 ONLY FROM C4
SELECT
C1,
C2,
SUM(C3) AS C3,
(SELECT TOP(1) C4 FROM TABLE1 AS B WHERE A.C1 = B.C1) AS C4
FROM
TABLE1 AS A
GROUP BY
C1,
C2;
GO
--CLEAN UP THE DATABASE, DROP THE SAMPLE TABLE
IF EXISTS(SELECT name FROM sys.tables WHERE object_id = OBJECT_ID(N'TABLE1')) DROP TABLE TABLE1;
GO
Let me know if this helps.
Assuming you mean the first record ordered by c4 (grouped by c1 and c2), then this will work establishing a row_number and using max with case:
with cte as (
select *,
row_number() over (partition by c1, c2 order by c4) rn
from yourtable
)
select c1, c2, sum(c3), max(case when rn = 1 then c4 end) c4
from cte
group by c1, c2
SQL Fiddle Demo
However, if you don't want to order by c4, then you need some other column to ensure the correct order of the results. Without an order by clause, there's no guarantee on how they are returned.
I hope you choose 'X' and 'Q' as those rows where inserted first, while grouping C1 and C2.
I would suggest you to add an identity column in your table and work based on it as given below.
Table:
DECLARE #DB TABLE (ID INT IDENTITY(1,1),C1 VARCHAR(10),C2 VARCHAR(10),C3 INT,C4 VARCHAR(10))
INSERT INTO #DB VALUES
('A','B',2,'X'),
('A','B',3,'Y'),
('C','D',4,'Q'),
('C','D',1,'P')
Code:
SELECT A.*,B.C4
FROM (
SELECT C1,C2,SUM(C3) C3 FROM #DB
GROUP BY C1,C2) A
JOIN
(
SELECT C1,C2,C4 FROM (
SELECT *,ROW_NUMBER() OVER (PARTITION BY C1,C2 ORDER BY ID) [ROW]
FROM #DB) LU WHERE LU.ROW = 1) B
ON A.C1 = B.C1 AND A.C2 = B.C2
Result:

Insert values into a new table with "position in group" column?

I have a table:
c1 c2 c3
----------
100 200
100 201
100 203
200 405
200 408
...and another table created with 3 columns.
I need to add the values from the above table to new table including a new column like,
c1 c2 c3
--------------
100 200 1
100 201 2
100 203 3
200 405 1
200 408 2
...and so on. c3 is dependent on c1 and when c1 is changed, it is to be set for 1 and continue to increment.
The second table is created and is empty.
I need to copy values of columns c1 and c2 of old table to the new table and at the same time, insert new values to c3 of second table.
insert into my_table2
select c1, c2
,row_number() over (partition by c1 order by c1, c2)
from my_table
This should be what you need:
Sample schema
CREATE TABLE test (
c1 INT NOT NULL,
c2 INT NOT NULL
);
INSERT INTO TEST VALUES
(100, 200), (100, 201), (100, 203), (200, 405), (200, 408)
Query
SELECT T1.C1, T1.C2, COUNT(*) AS C3
FROM test T1
JOIN test T2 ON T1.C1 = T2.C1 AND T1.C2 >= T2.C2
GROUP BY T1.C1, T1.C2