SQL create empty table and fill some rows with info - sql

Is it possible to create an empty table with a fixed number of rows (the number is defined in another table) and defined columns(col1 and col2) and then replace some of the rows in this empty table with the ones I get from a select?
I want the final structure to be like this :
col1 col2
----------------
empty empty
val11 val21
val12 val22
empty empty
val13 val23
empty empty
where I take val11, val21.... from another select. I want this table with empty values to be only local aka not create it anywhere else.

You can create a table with a variable number (say 5) of rows with empty fields;
about the ordering, you can not use any internal ordering, so you need to add a field to hold the position of the row.
For example:
SQL> create table tab_5(id number, col1 varchar2(50), col2 varchar2(50))
2 /
Table created.
SQL> insert into tab_5
2 select level, null, null
3 from dual
4 connect by level <= 5
5 /
5 rows created.
SQL> select * from tab_5 order by id;
ID COL1 COL2
---------- -------------------- --------------------
1
2
3
4
5
If you need to update some records in interlaced way, you can rely on your id
SQL> update tab_5
2 set col1 = 'something',
3 col2 = 'something else'
4 where mod(id, 2) = 0;
2 rows updated.
SQL> select * from tab_5 order by id;
ID COL1 COL2
---------- -------------------- --------------------
1
2 something something else
3
4 something something else
5
SQL>

I don't see any point in inserting a fixed number of empty records only to "replace" them later. Rather, a better idea would be to just INSERT the records you want and then add some null records afterwards.
First INSERT the records you want from the other table:
INSERT INTO yourTable (col1, col2)
SELECT col1, col2
FROM anotherTable
Then INSERT the "empty" records:
INSERT ALL
INTO yourTable (col1, col2) VALUES (null, null)
INTO yourTable (col1, col2) VALUES (null, null)
INTO yourTable (col1, col2) VALUES (null, null)
...
SELECT 1 FROM DUAL;

Related

How to remove duplicate values from a table

I'm working on pulling data to a table which has two data sets of data loaded to temp table and finally inserts into table. There are 2 records which are duplicates but i need both the records if their value on one column is same else delete it. Col1 and col4 are primary keys
col1 col2 col3 col4
--------------------
a ab abc x
a ab abc y
b ab abc y
b ab abc z
what i want is forget about col 2 and col3, check in col1 if row 1 and row 2 are same it should go check col4 and if row1 and row 2 are different it should display both row1 and row 2 even if row 2 and row 3 in col4 are same. so if the records are as i mentioned it should allow all 4 values but with the logic i wrote it is returning row1,row3,row4 because it is considering row2 and row3 of col 1 with row 2 row 3 of col 4 and displaying only 3 records but i want all records. Please help me how to write a logic in sql for this situation.
Based on what I have understood from your question, you could do something like this..
DELETE i
FROM YourTable i INNER JOIN
(
SELECT col1,col4
FROM YourTable
GROUP BY col1,col4
HAVING count(col2)>1
) t ON t.col1 = i.col1 AND t.col4 = i.col4
Live Demo Here
Or if ou want to keep only one record and remove other duplicate records, you could do like this..
;with cte as
(
SELECT *,row_number() over(partition by col1,col4 order by col1,col4) as rn
FROM YourTable
)
DELETE from cte where rn>1
Live Demo Here

In SQL, how to get MERGE to update relevant row(s) with a single row from a grouped set of returned results

I am using MERGE (Oracle) to do updates to records that match on criteria specified in the ON clause joined on a virtual table create by a subquery. Form of the statement is:
MERGE INTO table1 t1 USING SELECT (t2.f21, MAX(t2.f22), t3.f31, t3.f32
from
table2 t2, table3 t3
where
{... various join/filter criteria ...}
group by t2.f21, t3.f31, t3.f32) MATCHDATA
ON (t1.f11 = MATCHDATA.f21)
where t1.f12 = 'something';
Now the rub: MATCHDATA will return multiple rows because the "... criteria ..." will by nature return multiple groups of matching records. So the 'group by' along with the use of 'MAX()' is not buying me any guarantees; on the contrary, if I added:
where rownum = 1
after MATCHDATA after wrapping the MATCHDATA result in a another SELECT statement that simply repeated the returned field names, I would then be limiting myself to being able to update only the one record in the one group of records that needs updating that has the highest value as determined by MAX(). Instead, I need to have the records in table1 that match on the join field in each MAX() record for their group of records updated. I started on Fri. down the PARTITION BY path and am new to that one, so didn't make much headway. But it looks promising and maybe tomorrow will yield better results. As it is, when I try to use it without for example limiting the returned recordset in MATCHDATA to one record via use of "rownum = 1", I get that familiar "could not return a stable set of records" error that MERGE proponents (like myself) must smile sheepishly at when their colleagues come to them for advice on this "better-than-correlated-subqueries"-evangelized nouveau SQL command as they face this same error.
As you can see, I am treating MERGE as the more successful brother of the correlated subquery. But is this a case where I should be looking back to the lesser of two weevils (i.e., use a correlated subquery instead) to get the job done? Or is the fix to be found in PARTITION BY or another modification to the above?
Thanks to all who take the time to offer their advice, I appreciate it.
I get that familiar "could not return a stable set of records" error
Because the join key you have used in the ON clause is not enough to make the row unique to perform the WHEN MATCHED THEN UPDATE statement.
You must include more keys in the ON clause until the matched rows are unique and thus returning a stable set of records.
Let's see a test case:
Set up
SQL> CREATE TABLE source_table (
2 col1 NUMBER,
3 col2 VARCHAR2(10),
4 col3 VARCHAR2(10)
5 );
Table created.
SQL>
SQL> INSERT INTO source_table (col1, col2, col3) VALUES (1, 'a', 'p');
1 row created.
SQL> INSERT INTO source_table (col1, col2, col3) VALUES (1, 'b', 'q');
1 row created.
SQL> INSERT INTO source_table (col1, col2, col3) VALUES (2, 'c', 'r');
1 row created.
SQL> INSERT INTO source_table (col1, col2, col3) VALUES (3, 'c', 's');
1 row created.
SQL>
SQL> COMMIT;
Commit complete.
SQL>
SQL> CREATE TABLE target_table (
2 col1 NUMBER,
3 col2 VARCHAR2(10),
4 col3 VARCHAR2(10)
5 );
Table created.
SQL>
SQL> INSERT INTO target_table (col1, col2, col3) VALUES (1, 'b', 'p');
1 row created.
SQL> INSERT INTO target_table (col1, col2, col3) VALUES (3, 'd', 'q');
1 row created.
SQL>
SQL> COMMIT;
Commit complete.
SQL>
SQL> SELECT * FROM source_table;
COL1 COL2 COL3
---------- ---------- ----------
1 a p
1 b q
2 c r
3 c s
SQL> SELECT * FROM target_table;
COL1 COL2 COL3
---------- ---------- ----------
1 b p
3 d q
SQL>
Error reproduce
SQL> MERGE INTO target_table trg
2 USING source_table src
3 ON (trg.col1 = src.col1) -- Not Unique
4 WHEN MATCHED THEN UPDATE SET
5 trg.col2 = src.col2,
6 trg.col3 = src.col3
7 WHEN NOT MATCHED THEN INSERT
8 (
9 col1,
10 col2,
11 col3
12 )
13 VALUES
14 (
15 src.col1,
16 src.col2,
17 src.col3
18 );
USING source_table src
*
ERROR at line 2:
ORA-30926: unable to get a stable set of rows in the source tables
SQL>
So, as expected we get the error ORA-30926: unable to get a stable set of rows in the source tables
Let's make the ON clause unique.
SQL> MERGE INTO target_table trg
2 USING source_table src
3 ON (trg.col1 = src.col1
4 AND
5 trg.col2 = src.col2) -- Unique
6 WHEN MATCHED THEN UPDATE SET
7 trg.col3 = src.col3
8 WHEN NOT MATCHED THEN INSERT
9 (
10 col1,
11 col2,
12 col3
13 )
14 VALUES
15 (
16 src.col1,
17 src.col2,
18 src.col3
19 );
4 rows merged.
SQL> SELECT * FROM target_table;
COL1 COL2 COL3
---------- ---------- ----------
1 b q
3 d q
2 c r
3 c s
1 a p
SQL>
Problem solved!
Remember, you cannot update the columns which are referenced in the ON clause.
Let's say we have this table T2:
C1 C2 AMOUNT UF
-- -- ---------- ----------
A X 12 101
A Y 3 102
A Y 12 103
B X 7 104
B Y 9 105
I need to have the records in table1 that match on the join field in
each MAX() record for their group of records updated. I started on
Fri. down the PARTITION BY path and am new to that one, so didnt make
much headway.
This is good path and you can do this using function rank():
select * from (
select t2.*, rank() over (partition by c1 order by amount desc) rn from t2 )
where rn=1
C1 C2 AMOUNT UF RN
-- -- ---------- ---------- --
A X 12 101 1
A Y 12 103 1
B Y 9 105 1
But if your joining field for merge is only 'C1' then this set of records is not stable, because for C1='A'
we have two rows and Oracle looks sheepishly, it does not know which one interests you.
To resolve this you can use row_number()
instead of rank() - if it's all the same. But if this matters you need something more in order clause, for instance:
select * from (
select t2.*, rank() over (partition by c1 order by amount desc, c2) rn from t2 )
where rn = 1
C1 C2 AMOUNT UF RN
-- -- ---------- ---------- --
A X 12 101 1
B Y 9 105 1
This set of rows is stable, because for C1 there are no duplicates and you can use it in your merge.
merge into t1
using (
select * from (
select t2.*, rank() over (partition by c1 order by amount desc, c2) rn from t2 )
where rn=1) md
on (md.c1 = t1.c1)
when matched then update set t1.uf = md.uf
when not matched then insert (t1.c1, t1.uf)
values (md.c1, md.uf)

How to add leading spaces to output column

Here is my SQL statement:
SELECT col1 AS MYCOL FROM table 1
UNION
SELECT col2 AS MYCOL FROM table 2
I need to add some spaces to col2 of table2 in output results so it looks like a tree:
MYCOL
row 1
row 2
row 2.1
row 2.2
row 3
row 3.1
row 3.2
note: just ignore rows sort/order.. Tell me how to add spaces..
Thanks
You can use the Concat(...) Function:
SELECT col1 AS MYCOL FROM table 1
UNION
SELECT CONCAT(" ", col2) AS MYCOL FROM table 2
SQL's job is to produce the required dataset. Beautification should be done at the front-end.
You can create an extra column to indicate the level of a row and use it for the appropriate formatting.
SELECT col1 AS MYCOL, 1 AS LEVEL FROM table 1
UNION
SELECT col2 AS MYCOL, 2 AS LEVEL FROM table 2

Detect differences between two versions of the same table

I am looking for a method to detect differences between two versions of the same table.
Let's say I create copies of a live table at two different days:
Day 1:
CREATE TABLE table_1 AS SELECT * FROM table
Day 2:
CREATE TABLE table_2 AS SELECT * FROM table
The method should identify all rows added, deleted or updated between day 1 and day 2;
if possible the method should not use a RDBMS-specific feature;
Note: Exporting the content of the table to text files and comparing text files is fine, but I would like a SQL specific method.
Example:
create table table_1
(
col1 integer,
col2 char(10)
);
create table table_2
(
col1 integer,
col2 char(10)
);
insert into table_1 values ( 1, 'One' );
insert into table_1 values ( 2, 'Two' );
insert into table_1 values ( 3, 'Three' );
insert into table_2 values ( 1, 'One' );
insert into table_2 values ( 2, 'TWO' );
insert into table_2 values ( 4, 'Four' );
Differences between table_1 and table_2:
Added: Row ( 4, 'Four' )
Deleted: Row ( 3, 'Three' )
Updated: Row ( 2, 'Two' ) updated to ( 2, 'TWO' )
If you want differences in both directions. I am assuming you have an id, because you mention "updates" and you need a way to identify the same row. Here is a union all approach:
select t.id,
(case when sum(case when which = 't2' then 1 else 0 end) = 0
then 'InTable1-only'
when sum(case when which = 't1' then 1 else 0 end) = 0
then 'InTable2-only'
when max(col1) <> min(col1) or max(col2) = min(col2) or . . .
then 'Different'
else 'Same'
end)
from ((select 'table1' as which, t1.*
from table_1 t1
) union all
(select 'table2', t2.*
from table_2 t2
)
) t;
This is standard SQL. You can filter out the "same" records if you want to.
This assumes that all the columns have non-NULL values and that rows with a given id appear at most once in each table.
I think I found the answer - one can use this SQL statement to build a list of differences:
Note: "col1, col2" list must include all columns in the table
SELECT
MIN(table_name) as table_name, col1, col2
FROM
(
SELECT
'Table_1' as table_name, col1, col2
FROM Table_1 A
UNION ALL
SELECT
'Table_2' as table_name, col1, col2
FROM Table_2 B
)
tmp
GROUP BY col1, col2
HAVING COUNT(*) = 1
+------------+------+------------+
| table_name | col1 | col2 |
+------------+------+------------+
| Table_2 | 2 | TWO |
| Table_1 | 2 | Two |
| Table_1 | 3 | Three |
| Table_2 | 4 | Four |
+------------+------+------------+
In the example quoted in the question,
Row ( 4, 'Four' ) present in table_2 ; verdict row "Added"
Row ( 3, 'Three' ) present in table_1; verdict row "Deleted"
Row ( 2, 'Two' ) present in table_1 only; Row ( 2, 'TWO' ) present in table_2 only; if col1 is primary key then verdict "Updated"
If you can asasume that the table has a unique primary key then the following SQL statements will at the end have created three views which contain the changed, new and deleted IDs.
In the following "tbl1" is the old version and "tbl2" is the new version (same schema). The primary key in the table is assumed to be named "_id".
The intermediate view "_NewChanged" will contain both the new and changed IDs in the new version of the table and the view "_RemovedChanged" will contain both the removed and changed IDs in the new version.
To generate delta SQL statements is just a matter of programatically looping the respective views to create a delta set and do the delete, update, insert statemens that transforms the old version to the new version.
The solution have three caveats:
It will not pin-point exactly what columns have changed
The schema is assumed to be the same in the old and new version of the table
It assumes that the SQL statments INTERSECT and EXCEPT are available
To keep the code brief no comments have been inserted.
drop view if exists _NewChanged;
drop view if exists _RemovedChanged;
create view _NewChanged as
select * from tbl2
except
select * from tbl1;
create view _RemovedChanged as
select * from tbl1
except
select * from tbl2;
drop view if exists changed;
drop view if exists new;
drop view if exists deleted;
create view changed as
select _id from _NewChanged
intersect
select _id from _RemovedChanged;
create view new as
select _id from _NewChanged
except
select _id from _RemovedChanged;
create view deleted as
select _id from _RemovedChanged
except
select _id from _NewChanged;

SQL table search

I'm new to SQL and was just wondering how I would a value from a table only when it meets a certain condition.
I have a table that looks like the following (sorry for the rubbish formatting)
COL1 - COL2
1 - 3
1 - 2
2 - 2
3 - 3
3 - 4
I want to get values from COL1, but only if they don't have a specific value in COL2.
So for example if I didn't want the values where COL2 was 3, the only value that would be returned from COL1 would be 2.
Thanks for any help
To clarify, the two columns just store id's that reference other tables. I only want the values from COL1 that don't reference a specific values in COL2.
so when I saw I don't want the values where COL2 is equal to 3, this means the value '1' from COL1 won't be returned as on row 1 COL2 is 3 and 3 won't be returned from COL1 because on row4 COL2 is equal to 3
I think you look for something like this.
select COL1
from TABLE_FOO
where COL1 not in (
select COL1
from TABLE_FOO
where COL2 = 3
)
I think Specific value means the record having only one corresponding value in the col2.
2-2 is specific because it has a specific value.
select col1 from c group by col1 having count(col2) > 1
The above query gives all the col1 values which does not have a specific values.