I have created a lookup table which holds Insert statements. Some of the values in the values have apostrophes in them.
The value appears like this
'SCRW, PAN-HD PHIL, THR'D: 8-32. L: 3/8""'
Whole statement is:
INSERT INTO PARTS_BOMD (BOM_ID, ITEM_REV, ITEM_CN, ITEM_NUMBER, ITEMNUMBER, FINDNUM, QTY, ITEMDESCRIPTION, ITEMREV, ITEMSIZE, REFDES, BOMTEXT02, ITEMLIST21, SUMMARYCOMPLIANCE, BOMMULTITEXT30, BOMNOTES, ITEMLIST10, BOMLIST01, BOMLIST03, BOMLIST02, ITEMTEXT22, ITEMTEXT23, ITEMLIFECYCLEPHASE, ITEMP2MULTILIST05, ITEMTEXT15, RNUM) VALUES (2009034062,'31','ECO05447','1472096','1422042','100','4','SCRW, PAN-HD PHIL, THR'D: 8-32. L: 3/8""','A0 MPL03682','PC','PC','','6 out of 6 Compliant','Missing Info','Missing Info','','ZHLB','','','X','','','AREL','Yes','0.10220',582272);
This table have more than 4 million records and this issue occurs most of the time.
Is there any way I can change this apostrophe into two single quotes.
I am using SQL Server 2014.
Oracle scripts which generates these insert statements:
set serveroutput on size 100000
set feedback off
declare
v_table_name varchar2(30) := 'PARTS_BOMD'; -- Your Tablename
v_column_list varchar2(2000);
v_insert_list varchar2(2000);
v_ref_cur_columns varchar2(4000);
v_ref_cur_query varchar2(2000);
v_ref_cur_output varchar2(2000);
v_column_name varchar2(2000);
cursor c1 is select column_name, data_type from user_tab_columns where table_name = v_table_name order by column_id;
refcur sys_refcursor;
begin
for i in c1 loop
v_column_list := v_column_list||','||i.column_name;
if i.data_type = 'NUMBER' then
v_column_name := i.column_name;
elsif i.data_type = 'DATE' then
v_column_name := chr(39)||'to_date('||chr(39)||'||chr(39)'||'||to_char('||i.column_name||','||chr(39)||'dd/mm/yyyy hh:mi:ss'||chr(39)||')||chr(39)||'||chr(39)||', '||chr(39)||'||chr(39)||'||chr(39)||'dd/mm/rrrr hh:mi:ss'||chr(39)||'||chr(39)||'||chr(39)||')'||chr(39);
elsif i.data_type = 'VARCHAR2' then
v_column_name := 'chr(39)||'||i.column_name||'||chr(39)';
end if;
v_ref_cur_columns := v_ref_cur_columns||'||'||chr(39)||','||chr(39)||'||'||v_column_name;
end loop;
v_column_list := ltrim(v_column_list,',');
v_ref_cur_columns := substr(v_ref_cur_columns,8);
v_insert_list := 'INSERT INTO '||v_table_name||' ('||v_column_list||') VALUES ';
v_ref_cur_query := 'SELECT '||v_ref_cur_columns||' FROM '||v_table_name;
open refcur for v_ref_cur_query;
loop
fetch refcur into v_ref_cur_output;
exit when refcur%notfound;
v_ref_cur_output := '('||v_ref_cur_output||');';
v_ref_cur_output := replace(v_ref_cur_output,',,',',null,');
v_ref_cur_output := replace(v_ref_cur_output,'(,','(null,');
v_ref_cur_output := replace(v_ref_cur_output,',,)',',null)');
v_ref_cur_output := replace(v_ref_cur_output,'null,)','null,null)');
v_ref_cur_output := v_insert_list||v_ref_cur_output;
--dbms_output.put_line (v_ref_cur_output);
INSERT INTO BOM_INS_LOOKUP(LOOKUP_STATMENT)
VALUES (v_ref_cur_output);
COMMIT;
end loop;
end;
/
It ain't much but it does the job.
There really isn't an easy way to approach this problem generically. How would you go about processing a list of values like this one?: 'A',',','B' Is it one or two or three values? And there are two ways to split it into two values.
Here's an approach you might take by making some assumptions about the format.
declare #pos int;
declare #s varchar(1024);
declare myCursor for select <COLUMN> from T;
open myCursor;
while ##fetch_status = 0
begin
fetch next from myCursor into #s;
set #pos = charindex(#s, '''', #pos);
while #pos > 0
begin
/* if apostrophe is followed by comma plus apostrophe
then assume this is the delimiter */
if substring(#s +',''', #pos + 1, 2) <> ','''
set #s = stuff(#s, #pos, 1, '''''');
else
set #pos = #pos + 2;
set #pos = charindex(#s, '''', #pos);
end
update T set <COLUMN> = #s where current of myCursor;
end
close myCursor;
deallocate myCursor;
It would be better to avoid the problem in the first place by properly quoting the values as the INSERT queries are generated. There are many ways to export data from Oracle that can be readily picked up by SQL Server.
Related
Please what is the effective way for getting the rows where the first column (primary index) is populated but all other columns are null? The table has 25 columns and I want to avoid putting all column names in the WHERE clause.
Thanks.
One of many methods (this will give you a generated SQL to run, but you can amend the code just to get its results through insert select, for instance)
create table test_table_2000 ( charcol1 varchar(2000) );
replace procedure sp_find_blank_rows( in_database varchar(50), in_tablename varchar(50) )
begin
declare
l_sql varchar(2000);
declare
l_int integer;
set l_sql = 'select * from '||in_database||'.'||in_tablename||' where 1=1 ';
FOR fReq AS cReq CURSOR FOR
select * from Dbc.Columns where databaseName=in_database and TableName=in_tablename order by ColumnId
DO
IF l_int is null THEN
SET l_sql = l_sql || ' and ' || fReq.ColumnName || ' is not null ';
ELSE
SET l_sql = l_sql || ' and ' || fReq.ColumnName || ' is null ';
END IF;
set l_int = 1;
END FOR;
insert into test_table_2000 values ( l_sql );
end;
call sp_find_blank_rows('<your_database>','<your_table>');
The code for the function is as follows:
CREATE OR REPLACE FUNCTION FTTH_GETBUSZONECODEMULTI(
p_house_nbr IN VARCHAR2,
p_as_of_date IN DATE DEFAULT SYSDATE)
RETURN VARCHAR2
AS
CURSOR l_get_cur
IS
SELECT
LTRIM(RTRIM(BZO.B2FEDC)) BusinessZoneCode
FROM
[not wasting your time with business logic]
l_return VARCHAR2(32767);
BEGIN
--
FOR l_get_rec IN l_get_cur LOOP
l_return := l_return || '|' || l_get_rec.BusinessZoneCode;
END LOOP;
--
CASE
WHEN l_return IS NULL THEN RETURN NULL;
ELSE RETURN l_return || '|';
END CASE;
--
END FTTH_GETBUSZONECODEMULTI;
My attempt at translating it is below:
CREATE FUNCTION ftth_GETBUSZONECODEMULTI(
#p_house_nbr VARCHAR(4000),
#p_as_of_date DATETIME)
RETURNS VARCHAR(4000)
AS
BEGIN
SET #p_as_of_date = GETDATE()
DECLARE l_get_cur CURSOR LOCAL
FOR
SELECT
LTRIM(RTRIM(BZO.B2FEDC)) BusinessZoneCode
FROM
[not wasting your time with business logic]
DECLARE #l_return VARCHAR(MAX);
--
SET #l_return = isnull(#l_return, '') + '|' + ISNULL((FETCH BusinessZoneCode from l_get_cur), '');
--
if #l_return IS NULL begin RETURN NULL END
if #l_return is not null BEGIN RETURN isnull(#l_return, '') + '|' END;
END
The problem is with, I think, how I'm trying to FETCH the value - even if I put a "NEXT" in there it doesn't work right. I've tried like 6 different ways to arrange the FETCH and none of them work.
here is some changes in cursor usage, I might be wrong where you cursor loop end , so you might need to adjust it:
CREATE FUNCTION ftth_GETBUSZONECODEMULTI(
#p_house_nbr VARCHAR(4000),
#p_as_of_date DATETIME)
RETURNS VARCHAR(4000)
AS
BEGIN
declare #BusinessZoneCode varchar(500)
SET #p_as_of_date = GETDATE()
DECLARE l_get_cur CURSOR LOCAL
FOR
SELECT
LTRIM(RTRIM(BZO.B2FEDC)) BusinessZoneCode
FROM
[not wasting your time with business logic]
OPEN l_get_cur
fetch next from l_get_cur
into #BusinessZoneCode
WHILE ##FETCH_STATUS = 0
begin
DECLARE #l_return VARCHAR(MAX);
--
SET #l_return = isnull(#l_return, '') + '|' + ISNULL((#BusinessZoneCode), '');
--
if #l_return IS NULL begin RETURN NULL END
if #l_return is not null BEGIN RETURN isnull(#l_return, '') + '|' END
fetch next from l_get_cur
into #BusinessZoneCode
end
close l_get_cur
deallocate l_get_cur
END
I want to find records using like query but in reverse mode
For exa: I have one string ts5e434
And now in databse I have one column called geohash and its contan comma seperated values
1) "ts5e4,ts5,ts5e434"
2) "ab,ye"
3) "ts,thh"
4) "t"
So here I want to get 1, 3 and 4 no records because its partially matching string
exa like clause
SELECT
*
FROM
service_geohashes
WHERE
'ts5e434' LIKE geohashes
Can anyone help me
Thanks in advance
I created function "LikeAny" in MSSQL which looks like:
CREATE FUNCTION [dbo].[LikeAny](#text nvarchar(MAX), #delimiter varchar(20), #comparestring nvarchar(MAX))
RETURNS BIT AS
BEGIN
DECLARE #LikeAny BIT = 0,
#TempString nvarchar(MAX)
DECLARE MY_CURSOR CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT value FROM STRING_SPLIT(#text, #delimiter)
OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO #TempString
WHILE ##FETCH_STATUS = 0
BEGIN
--Do something with Id here
IF (#TempString <> '' AND #comparestring LIKE N'%' + #TempString + '%')
BEGIN
SET #LikeAny = 1
BREAK;
END
ELSE
FETCH NEXT FROM MY_CURSOR INTO #TempString
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR
RETURN #LikeAny
END
If you use this in your example, it should look like:
SELECT
*
FROM
service_geohashes
WHERE
[dbo].[LikeAny](geohashes ,',', 'ts5e434') = 1
I tried also to convert the function above into MySQL but I had no option to test it on real environment
it looks like:
DROP FUNCTION IF EXISTS LikeAnyCommaDelimited;
DELIMITER $$
CREATE FUNCTION LikeAnyCommaDelimited(p_text longtext, p_comparestring longtext)
RETURNS TINYINT
DETERMINISTIC
BEGIN
DECLARE v_finished INTEGER DEFAULT 0;
DECLARE v_LikeAny TINYINT DEFAULT 0;
DECLARE v_TempString longtext;
DECLARE v_SQL longtext;
drop temporary table if exists tempa;
drop temporary table if exists tempb;
CREATE TEMPORARY TABLE tempa( txt text );
CREATE TEMPORARY TABLE tempb( val char(255) );
insert into tempa values(p_text);
set v_SQL = concat("insert into tempb (val) values ('", replace(( select group_concat(distinct txt) as data from tempa), ',', "'),('"),"');");
prepare statement1 from #sql;
execute statement1;
DEClARE split_cursor CURSOR FOR
SELECT value FROM (select distinct(val) as value from tempb);
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET v_finished = 1;
OPEN split_cursor;
get_string: LOOP
FETCH split_cursor INTO v_TempString;
IF v_finished = 1 THEN
LEAVE get_string;
END IF;
IF (v_TempString <> '' AND p_comparestring LIKE N'%' + CONCAT(v_TempString , '%') THEN
BEGIN
SET v_LikeAny = 1;
LEAVE get_string;
END
END LOOP get_string;
CLOSE split_cursor;
END$$
DELIMITER ;
Let me know if you have any issues.
I am currently looping through values in PL/SQL with the following:
for c in (select * from example_table where name is not null) loop
-- logic
end loop;
I would like to replace the SQL statement with a dynamic one, for example:
l_sql := 'select * from example_table where || l_col || is not null';
for c in (l_sql) loop
-- logic
end loop;
Is this possible?
Best regards
That's not possible with an implicit cursor loop ( select inside for loop ). You may use the conventional OPEN .. FETCH .. LOOP through a REFCURSOR with a record variable of tablename%ROWTYPE
DECLARE
t_rec example_table%ROWTYPE;
l_sql VARCHAR2(1000);
v_cur SYS_REFCURSOR;
l_col varchar2(32) := 'MY_COLUMN';
BEGIN
l_sql := 'select * from example_table where '|| l_col || ' is not null';
OPEN v_cur FOR l_sql;
LOOP
FETCH v_cur INTO t_rec; --fetch a row
EXIT WHEN v_cur%NOTFOUND;
-- your logic using t_rec columns.
END LOOP;
CLOSE v_cur;
END;
/
I am not able to assign value to local variable from select statements. Please help me on this.
I want to assign value to local variable before cursor declaration. i want to use that local variable value in the cursor select statement.
have tried below code
CREATE OR REPLACE PROCEDURE TESTPROC(IN STARTKEY BIGINT, IN ENDKEY BIGINT)
BEGIN
DECLARE v_var1 VARCHAR(100);
SELECT status into v_var1 from emp where emp_id=1000; -- this will return only one value
DECLARE C_CURSOR CURSOR WITH HOLD FOR SELECT emp_code from company where status=v_var1;
OPEN C_CURSOR;
FETCH PV INTO C_CURSOR;
--
--
-- My logic
CLOSE C_CURSOR;
END
#
You could try using nested BEGIN/END blocks to assign the value to the variable before using it in the cursor. (This works in other RBDMS).
CREATE OR REPLACE PROCEDURE TESTPROC(IN STARTKEY BIGINT, IN ENDKEY BIGINT)
BEGIN -- Parent block
DECLARE v_var1 VARCHAR(100);
BEGIN -- Child block 1
SELECT status into v_var1 from emp where emp_id=1000;
END;
BEGIN -- Child block 2
DECLARE C_CURSOR CURSOR WITH HOLD FOR SELECT emp_code FROM company WHERE status = v_var1;
OPEN C_CURSOR;
FETCH PV INTO C_CURSOR;
-- Your logic ...
CLOSE C_CURSOR;
END;
END
#
Try this
CREATE OR REPLACE PROCEDURE TESTPROC(IN STARTKEY BIGINT, IN ENDKEY BIGINT)
language sql
specific testproc
BEGIN
DECLARE v_var1 VARCHAR(100);
DECLARE v_sql VARCHAR(1024);
DECLARE v_emp_code bigint;
DECLARE C_CURSOR CURSOR WITH HOLD FOR s1;
SELECT status into v_var1 from emp where emp_id=1000; -- this will return only one value
set v_sql ='SELECT emp_code from company where status = '''||v_var1||'''' ;
PREPARE s1 from v_sql;
OPEN C_CURSOR;
FETCH C_CURSOR into v_emp_code;
--
--
-- My logic
CLOSE C_CURSOR;
END
#
Just modify your query like this
SELECT f1.emp_code from company f1
inner join emp f2 on f1.status=f2.status and f2.emp_id=1000;