Shell script for insert multiple records into a Database - shell

I have a table in an Informix DB into which I want to insert multiple records at a time.
Data for one of the column should be unique & other column data may be the same for all the records I insert
Typical Insert Statement I use to insert one row :
insert into employee(empid, country, state) values(1, us, ca)
Now I want to pass different values for the 'empid' column, & data in rest of the columns can remain the same.
I am looking for something like looping empid & prompting user to enter the Start & End range of values for empid
When User enters Start Value as 1 & End Value as 100, the script should inset 100 records with empid's from 1 to 100

Please notice that you need to pass the start and end parameters as input arguments to your script:
#!/bin/sh
start=$1
end=$2
while [[ $start -le $end ]];
do
echo "insert into employee(empid, country, state) values(${start}, us, ca)"
start=`expr $start + 1`
done

This solution uses a stored procedure to wrap the insert statement in a loop. Probably more suitable for a large amount of data where performance is critical:
File InsertEmployee.sql
drop procedure InsertEmployee;
create procedure InsertEmployee(
p_start_empid like employee.empid,
p_end_empid like employee.empid,
p_country like employee.country,
p_state like employee.state
) returning char(255);
define i integer;
let i = p_start_empid;
while i <= p_end_empid
insert into employee (
empid,
country,
state
) values (
i,
p_country,
p_state
);
-- logging
-- if (DBINFO('sqlca.sqlerrd2') > 0) then
-- return "inserted empid=" || i || " country=" || p_country || " state=" || p_state with resume;
-- end if
let i = i + 1;
end while;
end procedure;
Load stored procedure into your database:
dbaccess mydatabasename InsertEmployee.sql
Call the stored procedure from the shell prompt:
echo 'execute procedure InsertEmployee(1,100,"us","ca");' | dbaccess mydatabasename

#!/bin/bash
declare -a namesArray=("name1","name2","name3")
inserts=""
for i in "{arr[#]}"
do
inserts+="INSERT INTO persons(id, name) VALUES (0,'$i');"
done
echo $inserts | dbaccess yourDataBase
That will insert 3 rows (i asume that your primary key is serial number, thats why is 0 in values field). In informix you cannot add multiple rows in the same insert, thats why i create an insert per row.
Informix: INSERT INTO table VALUES(0);
mySQL & SQL Server: INSERT INTO table VALUES(0),(1),(2); <- 3 rows

Related

Oracle equivalent query for this postgress query - CONFLICT [duplicate]

The UPSERT operation either updates or inserts a row in a table, depending if the table already has a row that matches the data:
if table t has a row exists that has key X:
update t set mystuff... where mykey=X
else
insert into t mystuff...
Since Oracle doesn't have a specific UPSERT statement, what's the best way to do this?
The MERGE statement merges data between two tables. Using DUAL
allows us to use this command. Note that this is not protected against concurrent access.
create or replace
procedure ups(xa number)
as
begin
merge into mergetest m using dual on (a = xa)
when not matched then insert (a,b) values (xa,1)
when matched then update set b = b+1;
end ups;
/
drop table mergetest;
create table mergetest(a number, b number);
call ups(10);
call ups(10);
call ups(20);
select * from mergetest;
A B
---------------------- ----------------------
10 2
20 1
The dual example above which is in PL/SQL was great becuase I wanted to do something similar, but I wanted it client side...so here is the SQL I used to send a similar statement direct from some C#
MERGE INTO Employee USING dual ON ( "id"=2097153 )
WHEN MATCHED THEN UPDATE SET "last"="smith" , "name"="john"
WHEN NOT MATCHED THEN INSERT ("id","last","name")
VALUES ( 2097153,"smith", "john" )
However from a C# perspective this provide to be slower than doing the update and seeing if the rows affected was 0 and doing the insert if it was.
An alternative to MERGE (the "old fashioned way"):
begin
insert into t (mykey, mystuff)
values ('X', 123);
exception
when dup_val_on_index then
update t
set mystuff = 123
where mykey = 'X';
end;
Another alternative without the exception check:
UPDATE tablename
SET val1 = in_val1,
val2 = in_val2
WHERE val3 = in_val3;
IF ( sql%rowcount = 0 )
THEN
INSERT INTO tablename
VALUES (in_val1, in_val2, in_val3);
END IF;
insert if not exists
update:
INSERT INTO mytable (id1, t1)
SELECT 11, 'x1' FROM DUAL
WHERE NOT EXISTS (SELECT id1 FROM mytble WHERE id1 = 11);
UPDATE mytable SET t1 = 'x1' WHERE id1 = 11;
None of the answers given so far is safe in the face of concurrent accesses, as pointed out in Tim Sylvester's comment, and will raise exceptions in case of races. To fix that, the insert/update combo must be wrapped in some kind of loop statement, so that in case of an exception the whole thing is retried.
As an example, here's how Grommit's code can be wrapped in a loop to make it safe when run concurrently:
PROCEDURE MyProc (
...
) IS
BEGIN
LOOP
BEGIN
MERGE INTO Employee USING dual ON ( "id"=2097153 )
WHEN MATCHED THEN UPDATE SET "last"="smith" , "name"="john"
WHEN NOT MATCHED THEN INSERT ("id","last","name")
VALUES ( 2097153,"smith", "john" );
EXIT; -- success? -> exit loop
EXCEPTION
WHEN NO_DATA_FOUND THEN -- the entry was concurrently deleted
NULL; -- exception? -> no op, i.e. continue looping
WHEN DUP_VAL_ON_INDEX THEN -- an entry was concurrently inserted
NULL; -- exception? -> no op, i.e. continue looping
END;
END LOOP;
END;
N.B. In transaction mode SERIALIZABLE, which I don't recommend btw, you might run into
ORA-08177: can't serialize access for this transaction exceptions instead.
I'd like Grommit answer, except it require dupe values. I found solution where it may appear once: http://forums.devshed.com/showpost.php?p=1182653&postcount=2
MERGE INTO KBS.NUFUS_MUHTARLIK B
USING (
SELECT '028-01' CILT, '25' SAYFA, '6' KUTUK, '46603404838' MERNIS_NO
FROM DUAL
) E
ON (B.MERNIS_NO = E.MERNIS_NO)
WHEN MATCHED THEN
UPDATE SET B.CILT = E.CILT, B.SAYFA = E.SAYFA, B.KUTUK = E.KUTUK
WHEN NOT MATCHED THEN
INSERT ( CILT, SAYFA, KUTUK, MERNIS_NO)
VALUES (E.CILT, E.SAYFA, E.KUTUK, E.MERNIS_NO);
I've been using the first code sample for years. Notice notfound rather than count.
UPDATE tablename SET val1 = in_val1, val2 = in_val2
WHERE val3 = in_val3;
IF ( sql%notfound ) THEN
INSERT INTO tablename
VALUES (in_val1, in_val2, in_val3);
END IF;
The code below is the possibly new and improved code
MERGE INTO tablename USING dual ON ( val3 = in_val3 )
WHEN MATCHED THEN UPDATE SET val1 = in_val1, val2 = in_val2
WHEN NOT MATCHED THEN INSERT
VALUES (in_val1, in_val2, in_val3)
In the first example the update does an index lookup. It has to, in order to update the right row. Oracle opens an implicit cursor, and we use it to wrap a corresponding insert so we know that the insert will only happen when the key does not exist. But the insert is an independent command and it has to do a second lookup. I don't know the inner workings of the merge command but since the command is a single unit, Oracle could execute the correct insert or update with a single index lookup.
I think merge is better when you do have some processing to be done that means taking data from some tables and updating a table, possibly inserting or deleting rows. But for the single row case, you may consider the first case since the syntax is more common.
A note regarding the two solutions that suggest:
1) Insert, if exception then update,
or
2) Update, if sql%rowcount = 0 then insert
The question of whether to insert or update first is also application dependent. Are you expecting more inserts or more updates? The one that is most likely to succeed should go first.
If you pick the wrong one you will get a bunch of unnecessary index reads. Not a huge deal but still something to consider.
Try this,
insert into b_building_property (
select
'AREA_IN_COMMON_USE_DOUBLE','Area in Common Use','DOUBLE', null, 9000, 9
from dual
)
minus
(
select * from b_building_property where id = 9
)
;
From http://www.praetoriate.com/oracle_tips_upserts.htm:
"In Oracle9i, an UPSERT can accomplish this task in a single statement:"
INSERT
FIRST WHEN
credit_limit >=100000
THEN INTO
rich_customers
VALUES(cust_id,cust_credit_limit)
INTO customers
ELSE
INTO customers SELECT * FROM new_customers;

Stored procedure is taking too much time to update the table columns

I have created a stored procedure which is taking too much of time to update the columns of the table. Say 3 hrs to update 2.5k records out of 43k records.
So can I reduce the time of updating the records. Below is my logic for the same.
procedure UPDATE_MST_INFO_BKC
(
P_SAPID IN NVARCHAR2
)
as
v_cityname varchar2(500):='';
v_neid varchar2(500):='';
v_latitude varchar2(500):='';
v_longitude varchar2(500):='';
v_structuretype varchar2(500):='';
v_jc_name varchar2(500):='';
v_jc_code varchar2(500):='';
v_company_code varchar2(500):='';
v_cnt number :=0;
begin
select count(*) into v_cnt from structure_enodeb_mapping where RJ_SAPID=P_SAPID and rownum=1;
if v_cnt > 0 then
begin
select RJ_CITY_NAME, RJ_NETWORK_ENTITY_ID,LATITUDE,LONGITUDE,RJ_STRUCTURE_TYPE,RJ_JC_NAME,RJ_JC_CODE,'6000'
into v_cityname,v_neid,v_latitude, v_longitude, v_structuretype,v_jc_name,v_jc_code,v_company_code from structure_enodeb_mapping where RJ_SAPID=P_SAPID and rownum=1;
update tbl_ipcolo_mast_info set
CITY_NAME = v_cityname,
NEID = v_neid,
FACILITY_LATITUDE = v_latitude,
FACILITY_LONGITUDE = v_longitude,
RJ_STRUCTURE_TYPE = v_structuretype,
RJ_JC_NAME = v_jc_name,
RJ_JC_CODE = v_jc_code,
COMPANY_CODE = v_company_code
where SAP_ID=P_SAPID;
end;
end if;
end UPDATE_MST_INFO_BKC;
What adjustments can I make to this?
As far as I understand your code, It is updating TBL_IPCOLO_MAST_INFO having SAP_ID = P_SAPID Means It is updating one record and you must be calling the procedure for each record.
It is a good practice of calling the procedure once and update all the record in one go. (In your case 2.5k records must be updated in one call of this procedure only)
For your requirement, Currently, I have updated the procedure code to only execute MERGE statement, which will be same as multiple SQLs in your question for single P_SAPID.
PROCEDURE UPDATE_MST_INFO_BKC (
P_SAPID IN NVARCHAR2
) AS
BEGIN
MERGE INTO TBL_IPCOLO_MAST_INFO I
USING (
SELECT
RJ_CITY_NAME,
RJ_NETWORK_ENTITY_ID,
LATITUDE,
LONGITUDE,
RJ_STRUCTURE_TYPE,
RJ_JC_NAME,
RJ_JC_CODE,
'6000' AS COMPANY_CODE,
RJ_SAPID
FROM
STRUCTURE_ENODEB_MAPPING
WHERE
RJ_SAPID = P_SAPID
AND ROWNUM = 1
)
O ON ( I.SAP_ID = O.RJ_SAPID )
WHEN MATCHED THEN
UPDATE SET I.CITY_NAME = O.RJ_CITY_NAME,
I.NEID = O.RJ_NETWORK_ENTITY_ID,
I.FACILITY_LATITUDE = O.LATITUDE,
I.FACILITY_LONGITUDE = O.LONGITUDE,
I.RJ_STRUCTURE_TYPE = O.RJ_STRUCTURE_TYPE,
I.RJ_JC_NAME = O.RJ_JC_NAME,
I.RJ_JC_CODE = O.RJ_JC_CODE,
I.COMPANY_CODE = O.COMPANY_CODE;
END UPDATE_MST_INFO_BKC;
Cheers!!
3 hours? That's way too much. Are sap_id columns indexed? Even if they aren't, data set of 43K rows is just too small.
How do you call that procedure? Is it part of another code, perhaps some unfortunate loop which does something row-by-row (which is, in turn, slow-by-slow)?
A few objections:
are all those variables' datatypes really varchar2(500)? Consider declaring them so that they'd take table column's datatype, e.g. v_cityname structure_enodeb_mapping.rj_city_name%type;. Also, there's no need to explicitly say that their value is null (:= ''), it is so by default
select statement which checks whether there's something in the table for that parameter's value should be rewritten to use EXISTS as it should perform better than rownum = 1 condition you used.
also, consider using exception handlers (no-data-found if there's no row for a certain ID; too-many-rows if there are two or more rows)
select statement that collects data into variables has the same condition; do you really expect more than a single row for each ID (passed as a parameter)?
Anyway, the whole procedure's code can be shortened to a single update statement:
update tbl_ipcolo_mst_info t set
(t.city_name, t.neid, ...) = (select s.rj_city_name,
s.rj_network_entity_id, ...
from structure_enodeb_mapping s
where s.rj_sapid = t.sap_id
)
where t.sap_id = p_sapid;
If there is something to be updated, it will be. If there's no matching t.sap_id, nothing will happen.

How to make first letter of every word as capital letter in SAP HANA

Do we have any method to make first letter of every word as capital letter for a column in SAP HANA?
i.e "ask question" to "Ask Question"
With HANA 2 SPS3, there is a built in function called "Initcap"
SELECT INITCAP('that''s a new function') FROM DUMMY;
That'S A New Function
There is no builtin function for that on SQL level.
You could however write a user-defined function to do just that.
Possibly this site gives you a clue? I'm not familiar with SAP HANA but possibly you can label it as title sentence and do a case normalisation on it?
In T-SQL you could do something like this:
SELECT
UPPER(LEFT(ColumnA,1))+LOWER(RIGHT(ColumnA,(LEN(ColumnA)-1)))
FROM Table1;
You tell the DBMS to make the first letter in upper case, and the rest of the string in lower case. The LEN is needed because I assume the length of the string is not static and in the way of the script it is handled dynamically. The + is CONCAT in other dialects. Regarding whitespaces in ColumnA and the capitalization of the 2nd or more word in you column, I'm trying some stuff that Michael Valentine Jones described on 1-12-2006 here, alsee see part of the code beneath. I get back to this as soon as I figured this out. I'm not sure yet how the union all select statement works. Will take some days (due to the weekend). Anyway, maybe this helps you. Change the REPLACE of ',' with ' ' or something.
-- Create temp table to test inserting values into
create table #t (num int)
-- Create a comma delimited string to test with
declare #str varchar(500)
select #str = '4,2,7,7834,45,24,45,77'
--------------------------------------------------------
---- Code to load the delimited string into a table ----
--------------------------------------------------------
-- Create insert for comma delimited values
declare #sql varchar(8000)
select #sql = 'insert into #t select '+ replace(#str,',',' union all select ')
-- Load values from comma delimited string into a table
exec ( #sql )
I found a piece of T-SQL script that creates a function 'ProperCase' that will do the trick by calling the function in your select statement. See beneath. I foun it here: http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=47718 (by jsmith8858).
create function ProperCase(#Text as varchar(8000))
returns varchar(8000)
as
begin
declare #Reset bit;
declare #Ret varchar(8000);
declare #i int;
declare #c char(1);
select #Reset = 1, #i=1, #Ret = '';
while (#i <= len(#Text))
select #c= substring(#Text,#i,1),
#Ret = #Ret + case when #Reset=1 then UPPER(#c) else LOWER(#c) end,
#Reset = case when #c like '[a-zA-Z]' then 0 else 1 end,
#i = #i +1
return #Ret
end
And for example if you do a SELECT dbo.ProperCase ('ask question WHy iS tHAT'), then you get 'Ask Question Why Is That' as a result.
Hopefully this gets you on your way.

Create PL/SQL script with 2 cursors, a parameter and give results from a table?

I need to create a script that puts a key number from table A (which will be used as a parameter later), then flow that parameter or key number into a query and then dump those results into a holding record or table for later manipulation and such. Because each fetch has more than 1 row (in reality there are 6 rows per query results or per claim key) I decided to use the Bulk Collect clause. Though my initial test on a different database worked, I have not yet figured out why the real script is not working.
Here is the test script that I used:
DECLARE
--Cursors--
CURSOR prod_id is select distinct(product_id) from product order by 1 asc;
CURSOR cursorValue(p_product_id NUMBER) IS
SELECT h.product_description,o.company_short_name
FROM company o,product h
WHERE o.product_id =h.product_id
AND h.product_id =p_product_id
AND h.product_id IS NOT NULL
ORDER by 2;
--Table to store Cursor data--
TYPE indx IS TABLE OF cursorValue%ROWTYPE
INDEX BY PLS_INTEGER;
indx_tab indx;
---Variable objects---
TotalIDs PLS_INTEGER;
TotalRows PLS_INTEGER := 0 ;
BEGIN
--PARAMETER CURSOR RUNS---
FOR prod_id2 in prod_id LOOP
dbms_output.put_line('Product ID: ' || prod_id2.product_id);
TotalIDs := prod_id%ROWCOUNT;
--FLOW PARAMETER TO SECOND CURSOR--
Open cursorValue(prod_id2.product_id);
Loop
Fetch cursorValue Bulk collect into indx_tab;
---data dump into table---
--dbms_output.put_line('PROD Description: ' || indx_tab.product_description|| ' ' ||'Company Name'|| indx_tab.company_short_name);
TotalRows := TotalRows + cursorValue%ROWCOUNT;
EXIT WHEN cursorValue%NOTFOUND;
End Loop;
CLOSE cursorValue;
End Loop;
dbms_output.put_line('Product ID Total: ' || TotalIDs);
dbms_output.put_line('Description Rows: ' || TotalRows);
END;
Test Script Results:
anonymous block completed
Product ID: 1
Product ID: 2
Product ID: 3
Product ID: 4
Product ID: 5
Product ID Total: 5
Description Rows: 6
Update: Marking question as "answered" Thanks.
The first error is on line 7. On line 4 you have:
CURSOR CUR_CLAIMNUM IS
SELECT DISTINCT(CLAIM_NO)FROM R7_OPENCLAIMS;
... and that seems to be valid, so your column name is CLAIM_NO. On line 7:
CURSOR OPEN_CLAIMS (CLAIM_NUM R7_OPENCLAIMS.CLAIM_NUM%TYPE) IS
... so you've mistyped the column name as CLAIM_NUM, which doesn't exist in that table. Which is what the error message is telling you, really.
The other errors are because the cursor is invalid, becuase of that typo.
When you open the second cursor you have the same name confusion:
OPEN OPEN_CLAIMS (CUR_CLAIMNUM2.CLAIM_NUM);
... which fails because the cursor is querying CLAIMNO not CLAIMNUM; except here it's further confused by the distinct. You haven't aliased the column name so Oracle applies one, which you could refer to, but it's simpler to add your own:
CURSOR CUR_CLAIMNUM IS
SELECT DISTINCT(CLAIM_NO) AS CLAIM_NO FROM R7_OPENCLAIMS;
and then
OPEN OPEN_CLAIMS (CUR_CLAIMNUM2.CLAIM_NO);
But I'd suggest you also change the cursor name from CUR_CLAIMNUM to CUR_CLAIM_NO, both in the definition and the loop declaration. And having the cursor iterator called CUR_CLAIMNUM2 is odd as it suggests that is itself a cursor name; maybe something like ROW_CLAIM_NO would be clearer.

SQL Server stored procedure slows every execution

I've got a stored procedure which does many selects and updates with some cursors.
When I execute the procedure the first time, it takes about 30 seconds. Second execution takes about 1 minute. Third about 2 minutes.
Every execution slows the procedure. Now it takes about 10 minutes.
What is going wrong?
Variables:
declare #StatistikStatus nvarchar(100)
declare #SQL as nvarchar(MAX)
declare #Datum as nvarchar(50) --Datum im nvarchar Format
declare #Datumdatetime datetime --Datum im datetime Format
declare #tickethistorieID as uniqueidentifier
declare #id int --ID der Terminauswertung. Wird bei Einträgen benötigt, die pro Ticket mehrere Termine vereinbart haben.
declare #nextTermin datetime --Wird bei Einträgen benötigt, die pro Ticket mehrere Termine vereinbart haben.
declare #status nvarchar(100)
declare #statusdiff as nvarchar(100)
declare #vorStatus as nvarchar(100)
declare #lastid as int
declare #tickethistoriemerker nvarchar(40)
declare #statistikstatusmerker nvarchar(100)
DECLARE #TicketID uniqueidentifier
Sample cursor:
DECLARE C_TicketHistorie CURSOR FOR
SELECT
dbo.TicketHistorie.TicketID,dbo.TicketHistorie.Datum,dbo.tickethistorie.tickethistorieid
FROM
dbo.TicketHistorie INNER JOIN
dbo.Status ON dbo.TicketHistorie.NeueStatusID = dbo.Status.StatusID
INNER JOIN dbo.StatuszuStatistikStatus as s on s.status_ID = dbo.Status.statusid
INNER JOIN dbo.StatistikStatus as ss on s.bewertung_id = ss.id
WHERE
ss.id = 5 AND -- 5 = HNR Terminbestätigung
(dbo.Status.Name = N'Termin vereinbart')
AND ((YEAR(dbo.TicketHistorie.Datum) >= 2011 and day(dbo.TicketHistorie.Datum) >= 27 and month(dbo.TicketHistorie.Datum) >= 12)or YEAR(dbo.TicketHistorie.Datum) >= 2012)
ORDER BY TicketID,Datum asc
OPEN C_TicketHistorie;
FETCH NEXT FROM C_TicketHistorie into #TicketID,#Datumdatetime,#TickethistorieID
WHILE ##FETCH_STATUS = 0
BEGIN
--some inserts etc.
FETCH NEXT FROM C_TicketHistorie into #TicketID,#Datumdatetime,#TickethistorieID
END
CLOSE C_TicketHistorie
DEALLOCATE C_TicketHistorie
I've got 4 cursors.
And some dynamix SQL like this
SET #SQL ='UPDATE Statistik.dbo.terminauswertungab27122011 SET ['
SET #SQL =#SQL + #StatistikStatus+']='''
SET #SQL =#SQL + cast(#TicketHistorieID as NVARCHAR(36))+''''
SET #SQL =#SQL + ' WHERE ID = ' + cast(#ID as nvarchar) +' and ['+#StatistikStatus+'] IS NULL'
EXEC (#SQL)
I call the procedure using SSMS.
at the beginning of the stp i delete the table where the inserts goes into. Then iam doing the Inserts. the table rows are the same every execution
First off, change your cursor declaration to a more efficient cursor:
DECLARE C_TicketHistorie CURSOR
LOCAL STATIC FORWARD_ONLY READ_ONLY
FOR
Next, are you sure that you need a cursor for these operations? It seems that your update, for example, could be accomplished as a single set-based operation instead of a cursor and dynamic SQL, especially if you know the set of column names that could be indicated by #StatistikStatus (where is this determined, by the way?). Here is how you could generate a set-based dynamic SQL update in one swoop instead of a cursor:
DECLARE #sql NVARCHAR(MAX) = N'';
WITH x AS
(
SELECT
-- why only use aliases for the tables you don't reference often?
th.TicketID, th.Datum, th.tickethistorieid
FROM
dbo.TicketHistorie AS th
INNER JOIN dbo.Status AS st
ON th.NeueStatusID = st.StatusID
INNER JOIN dbo.StatuszuStatistikStatus as s
on s.status_ID = st.statusid
INNER JOIN dbo.StatistikStatus as ss
on s.bewertung_id = ss.id
WHERE
ss.id = 5 -- 5 = HNR Terminbestätigung
AND st.Name = N'Termin vereinbart'
-- be smarter about date range queries!
AND dbo.TicketHistorie.Datum >= '2011127'
)
SELECT #sql += N'UPDATE Statistik.dbo.terminauswertungab27122011 SET ['
+ #StatistikStatus+']='''
+ cast(#TicketHistorieID as NVARCHAR(36))+''''
+ ' WHERE ID = ' + cast(#ID as nvarchar) + ' -- nvarchar(WHAT)?
and ['+#StatistikStatus+'] IS NULL;';
Probably a lot more optimization possible here, but as the comments suggest, tough to do without more info.

Resources