Pl/Sql array inside a statement - arrays

I'm trying to prepare a function, so I've started this sql sketch to figure out how to manage my situation:
DECLARE
x XMLType;
begin
x := XMLType('<?xml version="1.0"?>
<ROWSET>
<ROW>
<START_DATETIME>29/05/2015 14:23:00</START_DATETIME>
</ROW>
<ROW>
<START_DATETIME>29/05/2015 17:09:00</START_DATETIME>
</ROW>
</ROWSET>');
FOR r IN (
SELECT ExtractValue(Value(p),'/ROW/START_DATETIME/text()') as deleted
FROM TABLE(XMLSequence(Extract(x,'/ROWSET/ROW'))) p
) LOOP
-- do whatever you want with r.name, r.state, r.city
-- dbms_output.put_line( 'TO_DATE('''|| r.deleted ||''', '''|| 'DD/MM/YYYY HH24:MI:SS'')');
dbms_output.put_line( ''''|| r.deleted ||'''');
DELETE FROM MYTABLE a WHERE a.START_DATETIME not in (''''|| r.deleted || '''');
END LOOP;
END;
I've tried different ways to perform the query after the loop has filled the variable but is gaves me a conversion error:
00000 - "a non-numeric character was found where a numeric was expected"
*Cause: The input data to be converted using a date format model was
incorrect. The input data did not contain a number where a number was
required by the format model.
*Action: Fix the input data or the date format model to make sure the
elements match in number and type. Then retry the operation.
Can anybody help me?
thanks!

You're wrapping a string in explicit single quotes; that is making the quotes part of the string itself, which you don't want.
You need to convert the string to a data type, which you are sort of doing in a commented-out section - in that case you do need the extra quotes for your dbms_output() to make it a text literal, and to end up as a valid to_date() call; so you end up with output from that:
TO_DATE('29/05/2015 14:23:00', 'DD/MM/YYYY HH24:MI:SS')
But for your delete though you just need to do:
DELETE FROM MYTABLE a
WHERE a.START_DATETIME not in (to_date(r.deleted, 'DD/MM/YYYY HH24:MI:SS'));
The reference to r.deleted is already a string, so you refer to it directly, with no additional quotes.
You only have a single value though, so at that point in the loop using not in is not necessary and you can use != instead:
DELETE FROM MYTABLE
WHERE START_DATETIME != to_date(r.deleted, 'DD/MM/YYYY HH24:MI:SS');
Your title mentions an array, so perhaps you really intend to put all the values from the XML into a (schema-level type) table collection and then use that in the not in clause, so it removes everything except the dates in your XML. Doing it individually like this will effectively delete everything in the table if there is more than one date in the XML, which also suggests you either want to use an array, and/or actually meant in or = to only remove those.
Incidentally, extractValue() is deprecated, so it would be better to use XMLQuery or XMLTable, e.g.:
FOR r IN (
SELECT *
FROM XMLTable('/ROWSET/ROW/START_DATETIME'
PASSING x COLUMNS deleted VARCHAR2(19) PATH '.')
) LOOP

Related

SQL Server - add to this query to first check for existence of a string

I have an nvarchar field in my database called CatCustom which contains comma-separated 5-character codes. It can contain as little as one code, or as many as 20 codes, separated by commas.
Right now, I use this query to add a new 5-character code to the field in given records (in this case the new code is LRR01):
UPDATE dbo.Sources
SET CatCustom = CONCAT_WS(', ', RTRIM(CatCustom), 'LRR01')
WHERE SourceID IN (1,2,3,4,5,8,9,44,63,45,101,102,222,344)
I need to add to this though: I need the record to be updated only if that 5-character code doesn't already exist somewhere in the CatCustom field, to ensure that code is not in there more than once.
How would I accomplish this?
EDIT: I really don't understand how this can be considered a duplicate of the suggested thread. This is a VERY specific case and has nothing to do with creating stored procedures and or variables. The alleged duplicated thread does not really help me - sorry.
Use STRING_SPLIT function to split the comma separated list and then add Not Exist condition in the WHERE clause like below
UPDATE dbo.Sources
SET CatCustom = CONCAT_WS(', ', RTRIM(CatCustom), 'LRR01')
WHERE SourceID IN (1,2,3,4,5,8,9,44,63,45,101,102,222,344)
AND NOT EXISTS (SELECT 1 FROM STRING_SPLIT(CatCustom, ',') where value = 'LRR01')
UPDATE dbo.Sources
SET
CatCustom = CONCAT_WS(', ', RTRIM(CatCustom), 'LRR01')
WHERE
SourceID IN (1,2,3,4,5,8,9,44,63,45,101,102,222,344)
AND CatCustom NOT LIKE '%LRR01%';

SQL-manipulating strings

I'll try and make this clear...
Let's say I have a table with 2 columns. issue_number and issue_text. I need to grab 2 strings out of the issue_text column. The first string is something that can be hard coded with case statements since there are only so many types of issues that can be logged (note, i know this isn't the best way)
case
when issue_text like '%error%' then 'error'
else 'not found'
end as error_type
the issue_text is a string that will be formatted mostly the same, it'll have an error, more info, then an incident number, and that is the end of the string.
i.e. "Can't add address. Ref Number: 9999999"
the problem I'm having is the number will not always be the same amount of characters away from the error message.
I was wondering if there is a way to access the substring that causes a match from the like clause. like another case statement using a regex(which i know aren't supported well in sql)
case
when issue_text like '%[0-9 .]%' then (the substring match from like '%[0-9 .]%')
else 00000
end as issue_number
I am restricted to solving this issue and parsing these strings from SQL Server Management Studio or yes, I'd use .net or something to leverage.
Declare #YourTable table (ID int,issue_text varchar(150))
Insert Into #YourTable values
(1,'Can''t add address. Ref Number: 9999999'),
(2,'error')
Select ID
,Issue = Left(issue_text,PatIndex('%:%',issue_text+':')-1)
,IssueNo = substring(issue_text,PatIndex('%:%',issue_text+':')+2,25)
From #YourTable
Returns
ID Issue IssueNo
1 Can't add address. Ref Number 9999999
2 error
If there's always a space just before the number and the number is the last part of the string you can do
RIGHT(issue_text, CHARINDEX(' ', REVERSE(issue_text)) - 1)

How to delete the rows that contain string?

I want to replace a string in sql script. Im using this query
update [master].[dbo].[Test]
set Student_ID = REPLACE (Student_ID , '1|2_', '1|2_345_')
I think my query is correct but im getting this error "String or binary data would be truncated.The statement has been terminated"
I think this is happen because in column Student_ID have redundant data like "hdhvjf124rgrthrt". How to delete the rows that contain "hdhvjf124rgrthrt" stuffs?
"String or binary data would be truncated.The statement has been terminated"
This exception is usually means that your db field doesn't have enough
length.
I think after you replace string, result string length is larger than column data length. try change column size and run script again.
Try this:
update [master].[dbo].[Test]
set Student_ID = REPLACE (Student_ID , '1|2_', '1|2_345_')
WHERE Student_ID like '%' + '1|2_' + '%'
You can delete those records with:
DELETE FROM [master].[dbo].[Test]
WHERE Student_ID LIKE '%hdhvjf124rgrthrt%'
I don't think that's the problem though. I think the length of Student_ID is too short when you do the replace (since you are adding characters). What is the length of Student_ID and how long are the actual values of Student_ID?
You must have some data that already has a length of 252 or more.
Thus when you replace '1|2_' with '1|2_345_' you are pushing it over the 255 length as you are adding 4 extra characters, or more if more that one occurance of the old pattern exists.
Try running a query to see....
SELECT LEN(StudentId) FROM TEST ORDER BY 1 DESC
That's your problem truncation wise.
However, you have asked how to do the DELETE and #HoneyBadger has already shown you that.

SQL Server String extract based on pattern

I have string data in the following format:
MODELNUMBER=Z12345&HELLOWORLD=WY554&GADTYPE=PLA&ID=Z-12345
/DTYPE=PLA&ID=S-10758&UN_JTT_REDIRECT=UN_JTT_IOSV
and need to extract IDs based on two conditions
Starting after a pattern &ID=
Ending till the last character or
if it hits a & stop right there.
So in the above example I'm using the following code:
SUBSTRING(MyCol,(PATINDEX('%&id=%',[MyCol])+4),(LEN(MyCol) - PATINDEX('%&id%',[MyCol])))
Essentially looking the pattern &id=% and extract string after that till end of the line. Would anyone advise on how to handle the later part of the logic ..
My current results are
Z-12345
Z-12345&UN_JTT_REDIRECT=UN_JTT_IOSV
What I need is
Z-12345
Z-12345
Try this
SUBSTRING(MyCol, (PATINDEX('%[A-Z]-[0-9][0-9][0-9][0-9][0-9]%',[MyCol])),7)
if you run into performance issues add the where clause below
-- from Mytable
WHERE [MyCol] like '%[A-Z]-[0-9][0-9][0-9][0-9][0-9]%'
maybe not the most elegant solution but it works for me.
Correct syntax of PATINDEX
Here's one example how to do it:
select
substring(d.data, s.s, isnull(nullif(e.e,0),2000)-s.s) as ID,
d.data
from data d
cross apply (
select charindex('&ID=', d.data)+4 as s
) s
cross apply (
select charindex('&', d.data, s) as e
) e
where s.s > 4
This assumes there data column is varchar(2000) and the where clause leaves out any rows that don't have &ID=
The first cross apply searches for the start position, the second one for the end. The isnull+nulliff in the actual select handles the case where & is not found and replaces it with 2000 to make sure the whole string is returned.

Avoid Adding Duplicate Records

I m trying to write if statement to give error message if user try to add existing ID number.When i try to enter existing id i get error .untill here it s ok but when i type another id no and fill the fields(name,adress etc) it doesnt go to database.
METHOD add_employee.
DATA: IT_EMP TYPE TABLE OF ZEMPLOYEE_20.
DATA:WA_EMP TYPE ZEMPLOYEE_20.
Data: l_count type i value '2'.
SELECT * FROM ZEMPLOYEE_20 INTO TABLE IT_EMP.
LOOP AT IT_EMP INTO WA_EMP.
IF wa_emp-EMPLOYEE_ID eq pa_id.
l_count = l_count * '0'.
else.
l_count = l_count * '1'.
endif.
endloop.
If l_count eq '2'.
WA_EMP-EMPLOYEE_ID = C_ID.
WA_EMP-EMPLOYEE_NAME = C_NAME.
WA_EMP-EMPLOYEE_ADDRESS = C_ADD.
WA_EMP-EMPLOYEE_SALARY = C_SAL.
WA_EMP-EMPLOYEE_TYPE = C_TYPE.
APPEND wa_emp TO it_emp.
INSERT ZEMPLOYEE_20 FROM TABLE it_emp.
CALL FUNCTION 'POPUP_TO_DISPLAY_TEXT'
EXPORTING
TITEL = 'INFO'
TEXTLINE1 = 'Record Added Successfully.'.
elseif l_count eq '0'.
CALL FUNCTION 'POPUP_TO_DISPLAY_TEXT'
EXPORTING
TITEL = 'INFO'
TEXTLINE1 = 'Selected ID already in database.Please type another ID no.'.
ENDIF.
ENDMETHOD.
I'm not sure I'm getting your explanation. Why are you trying to re-insert all the existing entries back into the table? You're just trying to insert C_ID etc if it doesn't exist yet? Why do you need all the existing entries for that?
If so, throw out that select and the loop completely, you don't need it. You have a few options...
Just read the table with your single entry
SELECT SINGLE * FROM ztable INTO wa WITH KEY ID = C_ID etc.
IF SY-SUBRC = 0.
"this entry exists. popup!
ENDIF.
Use a modify statement
This will overwrite duplicate entries with new data (so non key fields may change this way), it wont fail. No need for a popup.
MODIFY ztable FROM wa.
Catch the SQL exceptions instead of making it dump
If the update fails because of an exception, you can always catch it and deal with exceptional situations.
TRY .
INSERT ztable FROM wa.
CATCH sapsql_array_insert_duprec.
"do your popup, the update failed because of duplicate records
ENDTRY.
I think there's a bug when appending in internal table 'IT_EMP' and inserting in 'ZEMPLOYEE_20' table.
Suppose you append the first time and then you insert. But when you append the second time you will have 2 records in 'IT_EMP' that are going to be inserted in 'ZEMPLOYEE_20'. That is because you don't refresh or clear the internal table and there you will have a runtime error.
According to SAP documentation on 'Inserting Lines into Tables ':
Inserting Several Lines
To insert several lines into a database table, use the following:
INSERT FROM TABLE [ACCEPTING DUPLICATE KEYS] . This
writes all lines of the internal table to the database table in
one single operation. The same rules apply to the line type of
as to the work area described above. If the system is able to
insert all of the lines from the internal table, SY-SUBRC is set to 0.
If one or more lines cannot be inserted because the database already
contains a line with the same primary key, a runtime error occurs.
Maybe the right direction here is trying to insert the work area directly but before you must check if record already exists using the primary key.
Check the SAP documentation on this issue clicking the link before.
On the other hand, once l_count is zero because of l_count = l_count * '0'. that value will never change to any other number making that you won't append or insert again.
why are you retrieving all entries from zemployee_20 ?
You can directly check wether the 'id' exists already or not by using select single. If exists, then show message, if not, add.
It is recommended to retrieve only one field when its needed and not the entire table with asterisc *
SELECT single employee_id FROM ZEMPLOYEE_20 where employee_id = p_id INTO v_id. ( or field in structure )
if sy-subrc = 0. "exists
"show message
else. "not existing id
"populate structure and then add record to Z table
endif.
Furthermore, l_count is not only unnecessary but also bad implemented.
You can directly use the insert query,if the sy-subrc is unsuccessful raise the error message.
WA_EMP-EMPLOYEE_ID = C_ID.
WA_EMP-EMPLOYEE_NAME = C_NAME.
WA_EMP-EMPLOYEE_ADDRESS = C_ADD.
WA_EMP-EMPLOYEE_SALARY = C_SAL.
WA_EMP-EMPLOYEE_TYPE = C_TYPE.
INSERT ZEMPLOYEE_20 FROM WA_EMP.
If sy-subrc <> 0.
Raise the Exception.
Endif.

Resources