Insert 1000 rows in single sql statment or procedure - sql

How should i write a single sql statement or a stored procedure,
To insert 1000 values in 1000 rows and same column with each column having different values (among those 1000)
Here is the query i wrote,
INSERT INTO a_b values
(
(SELECT max(a_b_id) + 1 from a_b),
1111,
(SELECT s_id FROM a_b WHERE s_id in ('0','1','2','3','4')),
0,
1,
sysdate,
sysdate,
0,
1,
null
);
like say, i have 1000 s_id's i want select them one by one and insert them in one particular column, each time creating a new row.
EX, in first row s_id should be 0 then in second row it should be 1 like that goes on till thousand, attached an image of sample database i am working with.

You can use connect by for this:
INSERT INTO a_b (s_id, col2, col3, ....)
select level, --<< this value will change for every row
1111,
sysdate,
... more columns ...
from dual
connect by level <= 1000;

you can use cross apply to get 1000 rows along with 1000 other columns to insert 1000 rows as below:
insert into a_b (column names...)
select (max(a_b_id) over()) +1 as MaxId, s_id from a_b a cross apply (select 0, 1,SYSDATETIME, SYSDATETIME, 0, 1, null) b where a.s_id('111','222')--condition

The below is a syntax error. You will never get something like that to work.
create table fff
( id int not null
);
insert fff values (select 1,7777,select 3, select 3);
So you need to break it up into chunks
DROP PROCEDURE IF EXISTS uspRunMe;
DELIMITER $$
CREATE PROCEDURE uspRunMe()
BEGIN
insert into a_b select max(a_b_id) + 1 from a_b;
insert into a_b values (1111);
insert into a_b SELECT s_id FROM a_b WHERE s_id in ('0','1','2','3','4');
insert into a_b values (0,1);
insert into a_b select sysdate,sysdate;
insert into a_b values (0,1,null);
END;$$
DELIMITER ;
Test it:
call uspRunMe();
The above is for MySQL. You have a few db engines tagged here.

Related

How can I disable entering the same values in two fields at the same time on a table in Oracle?

Let´s say I create this table:
CREATE TABLE MYTABLE (
id INT NOT NULL AUTO_INCREMENT,
Field1 VARCHAR(30),
Field2 NUMBER(10),
);
Then I will insert this values:
INSERT INTO MYTABLE VALUES(null, 'Value', 10);
What I want is to be able to do both of these inserts:
INSERT INTO MYTABLE VALUES(null, 'Value', 5);
/* This works as there isn´t a row with both Field1='Value' and Field2=5 at the same time */
INSERT INTO MYTABLE VALUES(null, 'Something', 10);
/* This works as there isn´t a row with both Field1='Something' and Field2=10 at the same time */
But I don´t want to be able to do this (repeat both the Field1 and Field2 values together):
INSERT INTO MYTABLE VALUES(null, 'Value', 10);
/* This doesn´t work as there is a row with both Field1='Value' and Field2=10 at the same time */
How can I achieve this behaviour in Oracle? I thought about using ASSERTIONS but they are not yet implemented in Oracle.
I don´t want to be able to do this (repeat both the Field1 and Field2 values together)
You can use a COMPOUND trigger:
CREATE OR REPLACE TRIGGER mytable__not_repeat_f1_and_f2
FOR UPDATE OR INSERT ON MyTable
COMPOUND TRIGGER
TYPE MyTable_Fields_Type IS RECORD(
rid ROWID,
field1 MyTable.Field1%TYPE,
field2 MyTable.Field2%TYPE
);
TYPE MyTable_Fields_Table_Type IS TABLE OF MyTable_Fields_Type;
fields MyTable_Fields_Table_Type := MyTable_Fields_Table_Type();
AFTER EACH ROW IS
BEGIN
fields.EXTEND;
fields(fields.COUNT) := MyTable_Fields_Type(
:NEW.ROWID,
:NEW.Field1,
:NEW.Field2
);
END AFTER EACH ROW;
AFTER STATEMENT IS
num_field1 PLS_INTEGER;
num_field2 PLS_INTEGER;
BEGIN
FOR i IN 1 .. fields.COUNT LOOP
SELECT COUNT( CASE WHEN Field1 = fields(i).Field1 THEN 1 END ),
COUNT( CASE WHEN Field2 = fields(i).Field2 THEN 1 END )
INTO num_field1,
num_field2
FROM MyTable
WHERE ROWID != fields(i).RID;
IF num_field1 > 0 AND num_field2 > 0 THEN
RAISE_APPLICATION_ERROR( -20000, 'Cannot have duplicate Field1 and Field2' );
END IF;
END LOOP;
END AFTER STATEMENT;
END;
/
Then, for the table:
CREATE TABLE MYTABLE (
id INT
GENERATED ALWAYS AS IDENTITY
PRIMARY KEY,
Field1 VARCHAR2(30),
Field2 NUMBER(10)
);
If you do:
INSERT INTO MyTable ( Field1, Field2 )
SELECT 'a', 1 FROM DUAL UNION ALL
SELECT 'b', 2 FROM DUAL UNION ALL
SELECT 'c', 3 FROM DUAL;
That works but then trying to do:
INSERT INTO MyTable ( Field1, Field2 ) VALUES ( 'b', 3 );
Would raise the exception:
ORA-20000: Cannot have duplicate Field1 and Field2
ORA-06512: at "SCHEMA_NAME.MYTABLE__NOT_REPEAT_F1_AND_F2", line 33
ORA-04088: error during execution of trigger 'SCHEMA_NAME.MYTABLE__NOT_REPEAT_F1_AND_F2'
But:
INSERT INTO MyTable ( Field1, Field2 ) VALUES ( 'b', 4 );
Would work since this doesn't repeat a Field1 and a Field2 value together.
db<>fiddle here
If you want each column to be unique, you can just use unique constraints:
CREATE TABLE MYTABLE (
id INT NOT NULL AUTO_INCREMENT,
Field1 VARCHAR(30) UNIQUE,
Field2 NUMBER(10) UNIQUE
);
Create a compound unique index containing both columns so that the combination can't be inserted more than once, whether it be at the same time or at different times.
I don't think there's any way to do this reliably. If I understand correctly, you want to reject an inserted row if Field1 and Field2 both already exist in the table, but not necessarily in the same row.
So if your table looks like:
Field1 Field2
------ ------
Value 5
Something 10
I should be able to insert ('Apple', 7), right? Then when i try to insert ('Value', 7), it fails because there are rows with both those values already.
But I could do them in the reverse order: insert ('Value', 7) and then ('Apple', 7)
So what happens if I do this?
WITH mydata AS (
SELECT 'Apple' AS field1, 7 AS field2 FROM dual UNION ALL
SELECT 'Value', 7 FROM dual
INSERT INTO mytable
SELECT null, field1, field2 FROM mydata
Does that succeed or fail? There's no way to know. You are not guaranteed that the database actions happen in the order in which you think they're going to happen.
You'll run into the same problem if two sessions try to insert these two rows, then commit.

Delete Result Rows from a Table in SQL

I have 2 tables named BM_Data1 & BM_Data1_May62019. Both Tables contains the same data as BM_Data1_May62019 is the copy of BM_Data1 table. But BM_Data1 has some extra rows, How can I delete those extra rows from BM_Data1 and make it same like BM_Data1_May62019.
I got the extra rows using the following query.
SELECT * FROM
(SELECT * FROM BM_DATA1
EXCEPT
SELECT * FROM BM_DATA1_MAY62019) a
There are 7803 extra rows, how can I delete them from BM_Data1 table?
Thank You
As you confirmed RECID is common in both table with unique value, you can try this following script
DELETE FROM BM_DATA1
WHERE RECID NOT IN
(
SELECT RECID FROM BM_DATA1_MAY62019
)
Use a MERGE statement with WHEN NOT MATCHED BY SOURCE THEN DELETE.
MERGE works like a JOIN of sorts, and you need to be able to identify which rows are equal. You do this by the ON clause - for you, that would be RECID.
I suggest you run these in a transaction first, so you verify that you only delete the data you intend to - and commit the transaction only when you are sure you have the right configuration. If something is wrong, you can rollback
BEGIN TRANSACTION
MERGE BM_DATA1 AS Target
USING BM_DATA1_MAY62019as Source
ON (Target.RECID = Source.RECID)
WHEN NOT MATCHED BY SOURCE
THEN DELETE;
SELECT * FROM BM_DATA1
-- ROLLBACK TRANSACTION -- Uncomment and use this if it deleted the wrong data
-- COMMIT -- Uncomment and use this if it deleted the right data!
I've included some DDL so you can run this for yourself to help you understand the example. The ID column is the one that is common to both tables and you are removing the rows in A that are not in B
create table #data_a( id int, val int)
create table #data_b( id int, val int)
insert into #data_a select 1, 1
insert into #data_a select 2, 4
insert into #data_a select 3, 5
insert into #data_a select 4, 5
insert into #data_a select 5, 5
insert into #data_a select 6, 5
insert into #data_a select 7, 5
insert into #data_a select 8, 5
insert into #data_b select 1, 1
insert into #data_b select 2, 4
insert into #data_b select 3, 5
insert into #data_b select 4, 5
insert into #data_b select 5, 5
-- delete the extra rows in A
delete a from #data_a as a
left join #data_b as b on a.id = b.id
where b.id is null
-- we can see the rows are no longer in B
select * from #data_a
select * from #data_b
drop table #data_a
drop table #data_b
create table t1(id int, demo int);
create table t2(id int, demo int);
insert into t1 values (1, 1);
insert into t2 values (1, 1);
insert into t1 values (2, 2);
insert into t2 values (2, 2);
insert into t1 values (3, 3);
insert into t2 values (3, 3);
insert into t1 values (4, 4); -- t1 table has some extra rows
insert into t1 values (5, 5); -- t1 table has some extra rows
insert into t1 values (6, 6); -- t1 table has some extra rows
To delete those records from first table which are not second table:
delete from t1 where id not in (select id from t2)
use just delete with a correlated subquery
delete from [BM_DATA1]
where not exists
(select 1
from [BM_DATA1_MAY62019]
where [BM_DATA1_MAY62019].RECID = [BM_DATA1].RECID -- put here here identified column name
)

SQL CTE Syntax to DELETE / INSERT rows

What's the CTE syntax to delete from a table, then insert to the same table and return the values of the insert?
Operating on 2 hours of sleep and something doesn't look right (besides the fact that this won't execute):
WITH delete_rows AS (
DELETE FROM <some_table> WHERE id = <id_value>
RETURNING *
)
SELECT * FROM delete_rows
UNION
(
INSERT INTO <some_table> ( id, text_field )
VALUES ( <id_value>, '<text_field_value>' )
RETURNING *
)
The expected behavior is to first clear all the records for an ID, then insert records for the same ID (intentionally not an upsert) and return those inserted records (not the deletions).
Your question update made clear that you cannot do this in a single statement.
Packed into CTEs of the same statement, both operations (INSERT and DELETE) would see the same snapshot of the table and execute virtually at the same time. I.e., the INSERT would still see all rows that you thought to be deleted already. The manual:
All the statements are executed with the same snapshot (see Chapter 13), so they cannot "see" one another's effects on the target tables.
You can wrap them as two independent statements into the same transaction - which doesn't seem strictly necessary either, but it would allow the whole operation to succeed / fail atomically:
BEGIN;
DELETE FROM <some_table> WHERE id = <id_value>;
INSERT INTO <some_table> (id, text_field)
VALUES ( <id_value>, '<text_field_value>')
RETURNING *;
COMMIT;
Now, the INSERT can see the results of the DELETE.
CREATE TABLE test_table (value TEXT UNIQUE);
INSERT INTO test_table SELECT 'value 1';
INSERT INTO test_table SELECT 'value 2';
WITH delete_row AS (DELETE FROM test_table WHERE value='value 2' RETURNING 0)
INSERT INTO test_table
SELECT DISTINCT 'value 2'
FROM (SELECT 'dummy') dummy
LEFT OUTER JOIN delete_row ON TRUE
RETURNING *;
The query above handles the situations when DELETE deletes 0/1/some rows.
Elaborating on skif1979's "DelSert" CTE method, the "Logged DelSert:"
-- setups
DROP TABLE IF EXISTS _zx_t1 ;
CREATE TEMP TABLE
IF NOT EXISTS
_zx_t1
( id bigint
, fld2 bigint
, UNIQUE (id)
);
-- unique records
INSERT INTO _zx_t1 SELECT 1, 99;
INSERT INTO _zx_t1 SELECT 2, 98;
WITH
_cte_del_row AS
( DELETE
FROM _zx_t1
WHERE id = 2
RETURNING id as _b4_id, fld2 as _b4_fld2 -- returns complete deleted row
)
, _cte_delsert AS
( INSERT
INTO _zx_t1
SELECT DISTINCT
_cte_del_row._b4_id
, _cte_del_row._b4_fld2 + 1
from (SELECT null::integer AS _zunk) _zunk -- skif1979's trick here
LEFT OUTER JOIN _cte_del_row -- clever LOJ magic
ON TRUE -- LOJ cartesian product
RETURNING id as _aft_id , fld2 as _aft_fld2 -- return newly "delserted" rows
)
SELECT * -- returns before & after snapshots from CTE's
FROM
_cte_del_row
, _cte_delsert ;
RESULT:
_b4_id | _b4_fld2 | _aft_id | _aft_fld2
--------+----------+---------+-----------
2 | 209 | 2 | 210
AFAICT these all occur linearly w/in a unit of work, akin to a journaled or logged update.
Workable for
Child records
OR Schema w/ no FK
OR FK w/ cascading deletes
Not workable for
Parent records w/ FK & no cascading deletes
A related (& IMO better) answer, akin to the "Logged DelSert" is this, a logged "SelUp" :
-- setups
DROP TABLE IF EXISTS _zx_t1 ;
CREATE TEMP TABLE
IF NOT EXISTS
_zx_t1
( id bigint
, fld2 bigint
, UNIQUE (id)
);
-- unique records
INSERT INTO _zx_t1 SELECT 1, 99;
INSERT INTO _zx_t1 SELECT 2, 98;
WITH
_cte_sel_row AS
( SELECT -- start unit of work with read
id as _b4_id -- fields need to be aliased
,fld2 as _b4_fld2 -- to prevent ambiguous column errors
FROM _zx_t1
WHERE id = 2
FOR UPDATE
)
, _cte_sel_up_ret AS -- we're in the same UOW
( UPDATE _zx_t1 -- actual table
SET fld2 = _b4_fld2 + 1 -- some actual work
FROM _cte_sel_row
WHERE id = _b4_id
AND fld2 < _b4_fld2 + 1 -- gratuitous but illustrates the point
RETURNING id as _aft_id, fld2 as _aft_fld2
)
SELECT
_cte_sel_row._b4_id
,_cte_sel_row._b4_fld2 -- before
,_cte_sel_up_ret._aft_id
,_cte_sel_up_ret._aft_fld2 -- after
FROM _cte_sel_up_ret
INNER JOIN _cte_sel_row
ON TRUE AND _cte_sel_row._b4_id = _cte_sel_up_ret._aft_id
;
RESULT:
_b4_id | _b4_fld2 | _aft_id | _aft_fld2
--------+----------+---------+-----------
2 | 209 | 2 | 210
See also:
https://rob.conery.io/2018/08/13/transactional-data-operations-in-postgresql-using-common-table-expressions/

Stored Procedure Insert (Select and Values)

I'm looking to have a stored procedure that will:
run through Table A and retrieve all IDs.
insert into Table B all IDs (loop) but also static values which aren't found in Table A.
How do I approach this?
CREATE OR REPLACE PROCEDURE TEST AS
BEGIN
select ID from TABLE A;
INSERT INTO TABLE B
(
created_date,
created_by,
ID
)
VALUES ('sysdate', '1', 'RESULTS FROM SELECT QUERY');
END TEST;
Not sure how to merge static data ('sysdate' and '1') with results from a query.
No need for 2 separate queries. This should work with INSERT INTO SELECT:
INSERT INTO TABLEB
(
created_date,
created_by,
ID
)
SELECT 'sysdate', '1', id
FROM TABLEA

SQL Query to return rows where a list of numbers is between start and end values

There is a table in Oracle with the columns:
id | start_number | end_number
---+--------------+------------
1 | 100 | 200
2 | 151 | 200
3 | 25 | 49
4 | 98 | 99
5 | 49 | 100
There is a list of numbers (50, 99, 150).
I want an sql statement that returns all the ids where any of the numbers in the list of numbers is found equal to or between the start_number and the end_number.
Using the above example; 1, 4 and 5 should be returned.
1 - 150 is between or equal to 100 and 200
2 - none of the numbers are between or equal to 151 and 200
3 - none of the numbers are between or equal to 25 and 49
4 - 99 is between or equal to 98 and 99
5 - 50 and 99 are between or equal to 49 and 100
drop table TEMP_TABLE;
create table TEMP_TABLE(
THE_ID number,
THE_START number,
THE_END number
);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (1, 100, 200);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (2, 151, 200);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (3, 25, 49);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (4, 98, 99);
insert into TEMP_TABLE(the_id, the_start, the_end) values (5, 49, 100);
The following is the solution I came up with based on the comments and answers below plus some additional research:
SELECT
*
from
TEMP_TABLE
where
EXISTS (select * from(
select column_value as id
from table(SYS.DBMS_DEBUG_VC2COLL(50,99,150))
)
where id
BETWEEN TEMP_TABLE.the_start AND TEMP_TABLE.the_end
)
This works too:
SELECT
*
from
TEMP_TABLE
where
EXISTS (select * from(
select column_value as id
from table(sys.ku$_vcnt(50,99,150))
)
where id
BETWEEN TEMP_TABLE.the_start AND TEMP_TABLE.the_end
)
Here is a full example:
create table #list (
number int
)
create table #table (
id int,
start_number int,
end_number int
)
insert into #list values(50)
insert into #list values(99)
insert into #list values(150)
insert into #table values(1,100,200)
insert into #table values(2,151,200)
insert into #table values(3,25,49)
insert into #table values(4,98,99)
insert into #table values(5,49,100)
select distinct a.* from #table a
inner join #list l --your list of numbers
on l.number between a.start_number and a.end_number
drop table #list
drop table #table
You'll simply need to remove the code about #table (create, insert and drop) and put your table in the select.
It partly depends on how your are storing your list of numbers. I'll assume that they're in another table for now, as even then you have many options.
SELECT
*
FROM
yourTable
WHERE
EXISTS (SELECT * FROM yourList WHERE number BETWEEN yourTable.start_number AND yourTable.end_number)
Or...
SELECT
*
FROM
yourTable
INNER JOIN
yourList
ON yourList.number BETWEEN yourTable.start_number AND yourTable.end_number
Both of those are the simplest expressions, and work well for small data sets. If your list of numbers is relatively small, and your original data is relatively large, however, this may not scale well. This is because both of the above scan the whole of yourTable and then check each record against yourList.
What may be preferable is to scan the list, and then attempt to use indexes to check against the original data. This would require you to be able to reverse the BETWEEN statement to yourTable.start_number BETWEEN x and y
This can only be done if you know the maximum gap between start_number and end_number.
SELECT
*
FROM
yourList
INNER JOIN
yourTable
ON yourTable.end_number >= yourList.number
AND yourTable.start_number <= yourList.number
AND yourTable.start_number >= yourList.number - max_gap
To achieve this I would store the value of max_gap in another table, and update it as the values in yourTable change.
You will want to create a temporary table to hold your numbers, if the numbers aren't already in one. Then it becomes relatively simple:
SELECT DISTINCT mt.ID FROM MyTable mt
INNER JOIN TempTable tt --your list of numbers
ON tt.number Between mt.start_number and mt.end_number
To create the table based on an array of passed values, you can use table definitions in your procedure. I'm light on Oracle syntax and don't have TOAD handy, but you should be able to get something like this to work:
CREATE OR REPLACE PROCEDURE FindIdsFromList
AS
DECLARE
TYPE NumberRecord IS RECORD (Number int NOT NULL)
TYPE NumberList IS TABLE OF NumberRecord;
NumberList myNumberList;
BEGIN
myNumberList := (50,99,150);
SELECT DISTINCT mt.ID FROM MyTable mt
INNER JOIN myNumberList nt --your list of numbers
ON nt.Number Between mt.start_number and mt.end_number
END