Error when trying to find ANY of int array - arrays

I'm currently working on a function in PostgreSQL, where it takes in an array of integers. Everything in the function is working as expected, however at one point in the function I do the following:
EXECUTE
'INSERT INTO tmptable (user_id)
SELECT DISTINCT user_id FROM user_skills
WHERE skill_values_id=ANY('||selected_skills||')';
My function is able to read the array at other points in the code, however this part throws the following error:
Procedure execution failed
ERROR: malformed array literal: "
INSERT INTO tmptable (user_id)
SELECT DISTINCT user_id FROM user_skills
WHERE skill_values_id=ANY("
And finally- there is a line at the bottom of the error message that says:
DETAIL: Array value must start with "{" or dimension information.
Any ideas how to get the any and integer array to play nice? I'm assuming it has something to do with the || concentration casting it to a string?

Don't concatenate values, use parameters instead:
EXECUTE
'INSERT INTO tmptable (user_id)
SELECT DISTINCT user_id FROM user_skills
WHERE skill_values_id=ANY($1)'
using selected_skills;
More details in the manual:
https://www.postgresql.org/docs/current/static/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN

Related

SSRS parameter for multiple value GUIDs used in query throws odd error but works when I manually put GUIDs in the query

My query used in an SSRS report:
SELECT DISTINCT
asm.AssemblyCode,
asm.AssemblyRestorationDesc,
asm.AssemblyUnit,
asm.AssemblyGuid,
(SELECT SUM(m.MarkerQuantity)
FROM Marker m
WHERE m.AssemblyGuid = asm.AssemblyGuid
AND m.MarkerExcludeFromScope = 'False'
AND m.ContractGuid IN (#Contract)
AND m.Deleted = 0) AS Quantity,
(SELECT SUM(smkr.MarkerQuantity)
FROM BaselineMarker smkr
WHERE smkr.AssemblyCode = asm.AssemblyCode
AND smkr.MarkerExcludeFromScope = 'False'
AND smkr.Deleted = 0) AS BaselineQuantity,
(SELECT SUM(sa.AllowanceQuantity)
FROM BaselineAllowance sa
WHERE sa.AssemblyCode = asm.AssemblyCode
AND sa.Deleted = 0) AS AllowanceQuantity
FROM
Assembly asm
WHERE
asm.Deleted = 0
ORDER BY
asm.AssemblyCode, asm.AssemblyRestorationDesc, asm.AssemblyUnit, asm.AssemblyGuid
Throws an odd error that isn't really making sense given that the report works if I select a single value for the #Contract parameter instead of multiple values, which tells me the parameter itself is set up fine. Also, if I set the parameter itself to a single value parameter instead of calling multiple values, it works fine too.
The report also works if I manually enter the GUIDs by replacing the line AND m.ContractGuid IN (#Contract) with:
AND m.ContractGuid IN ('60A38AD0-F24F-DF11-A19C-00111103CDB5', 'F8C018CA-A00C-4BB1-B920-D460786F6820')
Could the parameter not be returning a list in the correct format?
Nothing should be odd about the parameter. It's Name is Contract, its prompt is Contract, its data type is text, it allows multiple values, the values are taken from a simple query. The return value is the contract guid, and the return prompt is the contract name.
The error I get is:
An error has occurred during report processing. (rsProcessingAborted)
Query execution failed for dataset 'RestorationSummary'.
(rsErrorExecutingCommand) Must declare the scalar variable
"#Contract". Incorrect syntax near the keyword 'AS'. Incorrect syntax
near the keyword 'AS'.

Find records where string values are similar

I am aggregating errors in order to track total number of errors being logged. I am currently trying to create a query that finds/groups several records that have similar value in order to record them as one error and not several individual ones. The only difference is an id or 2, which is why they are being grouped. The database gets injected with errors from our system via app insights and analytic stream. I have a table that holds the "template" error that will be used to find and group those specific errors. Not all errors being recorded need a template because they are being grouped appropriately because they are exactly the same. When trying to use like to find the errors, it seems to be having a hard time with the dashes. I am having a hard time finding info to help me with this issue. I've tried to use the replace for removing the dashes, but that doesn't work because the errors are too long.
Sample template:
Auto Resubmit for % failed with ' Object reference not set to an instance of an object. '
Sample error:
Auto Resubmit for 004e9e2d-3704-4cfd-a90d-42520203df79 - 18723191 failed with ' Object reference not set to an instance of an object. '
Auto Resubmit for 0130e64e-64e6-4a23-88a4-51fba823705b - 18734821 failed with ' Object reference not set to an instance of an object. '
Auto Resubmit for 11809bf5672f4e98987119dbd06e5d78 - 17359076 failed with ' Object reference not set to an instance of an object. '
Sample Query:
select top 1000 * from errorTable where error like 'Auto Resubmit for % failed with '' Object reference not set to an instance of an object.'
If the only thing that's differing is the parameter values, you could normalize the string before storing/checking it. There are lots of utilities around that can do that. One example is the ExtractSQLTemplate function in our free SDU Tools. You don't need to use the toolkit. Just grab the code for that function as an example. You can see it here: https://youtu.be/yX5q00m_uCA
The other option is to use a full text index on the string instead. You can use functions like FREETEXTTABLE and/or FREETEXT to find similarities between the strings.
One of the main reason that you are not able to use Template Table is that you have basically fail the very purpose of Template by storing error in this manner.
In other word, your table are not Normalize nor any relation is define between
2 tables.
Auto Resubmit for 004e9e2d-3704-4cfd-a90d-42520203df79 - 18723191 failed with
' Object reference not set to an instance of an object. '
Your error table design should be like,
TemplateID -- id column of Template table
Module -- Name of module from where error originated
SubmoduleName-- Method name
ErrorMessage-- Original error message like "Object reference not set to an instance of an object".
CustomError -- Auto Resubmit for 004e9e2d-3704-4cfd-a90d-42520203df79 - 18723191 failed with
This way you can easily join Template Table with Error Table using column ErrorMessage.
ErrorMessage : It should always contain original message thrown by `exception`.
You can customize your Error Table as per your requirement.It will take only little effort.
It would be very nice if you can store TemplateID (FK) in Error table.
All problem will vanish at once.
In short keep table in such a manner that it is easy to join them without string manipulation.
It is not clear, in what pattern % will be replace.
create table #Template(Templateid int identity(1,1),Template varchar(500))
insert into #Template values ('Auto Resubmit for % failed with ''Object reference not set to an instance of an object.''')
--select * from #Template
create table #ErrorLog(Errorid int identity(1,1),ErrorMessage varchar(500))
insert into #ErrorLog values
('Auto Resubmit for 004e9e2d-3704-4cfd-a90d-42520203df79 - 18723191 failed with ''Object reference not set to an instance of an object.''')
,('Auto Resubmit for 0130e64e-64e6-4a23-88a4-51fba823705b - 18734821 failed with ''Object reference not set to an instance of an object.''')
,('Auto Resubmit for 11809bf5672f4e98987119dbd06e5d78 - 17359076 failed with ''Object reference not set to an instance of an object.''' )
,('Auto Resubmit for itemid2 - 18137385 failed with '' Access Denied: userId=''12602174''' )
Use any split string function to split Template table by '%'
and store it in #temp table
create table #temp(Errorid int identity(1,1),ErrMsg varchar(500),rownum int)
insert into #temp
select value,row_number()over(order by (Select null))rn from #Template t
cross apply(select replace(value,'failed with','')value from string_split(t.Template,'%'))ca
select t1.* from
(
select el.ErrorMessage,c.ErrMsg,c.rownum from #ErrorLog EL
inner join #temp c on el.ErrorMessage like '%' +ErrMsg+'%' and c.rownum=1
)t1
inner join #temp c1 on t1.ErrorMessage like '%' +ltrim(c1.ErrMsg)+'%' and c1.rownum=2
select * from #temp
drop table #Template,#ErrorLog,#temp
In fact,for writing near perfect query 1 need to analyze data and write as many Test cases for it.

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.

h2: "data conversion error" on array returned from stored procedure

This is a followup post to this post. I am writing an accounting system backed by an h2 database. The tree of accounts is stored in the ACCOUNTS table, with the PARENT_ID column storing the links in the tree.
To get the path to a given node in the tree, I have the following stored procedure:
public static Long[] getAncestorPKs(Long id)
whose job is to produce an array of integers, being the PARENT_ID values between the given node and the root of the tree. Let's imagine it is defined like this (because I have tried this and I get the same error):
public static Long[] getAncestorPKs(Long id)
{
return new Long[]{new Long(1), new Long(2), new Long(3)};
}
It is properly registered in the database and I can call it from within a SQL query. My problem is that h2 seems to be unable to deal with the return value: if I use it like this:
SELECT ID FROM ACCOUNTS WHERE ID IN (ANCESTOR_PKS(5))
then I get the following error:
Data conversion error converting "(1, 2, 3)"; SQL statement:
SELECT ID FROM ACCOUNTS WHERE ID IN (ANCESTOR_PKS(5)) [22018-167]
If, instead, I send the following to the database:
SELECT ID FROM ACCOUNTS WHERE ID IN (1, 2, 3)
I get back a result set with 3 rows, containing the three integers (exactly what I expect).
I really can't see what is the problem here! I am returning an array of Longs, which are to be used in comparing against a column which contains BIGINTS. Why is h2 refusing to convert this array? I have tried making the return value be Object[], because the h2 documentation is not entirely clear whether this is required on the return side as well as on the call side, but that makes no difference at all. I'm just banging my head against a brick wall here. This ain't rocket science! Surely someone has written similar code before?
Many thanks in advance, before I go mad!
If the method returns an array of objects, then for the database this is one value of data type ARRAY. And not a table with 3 rows. But of course you don't use the data type ARRAY in your table, you use INT or BIGINT. So your query is incorrect.
Either the method needs to return a ResultSet, or you need to convert the array value to a table. To do that, you could use the function TABLE(..) as follows:
select x from table(x bigint = getAncestorPKs(1));
So what you could do is:
drop table accounts;
create table accounts(id int);
insert into accounts values(1), (2), (10), (20);
drop alias getAncestorPKs;
create alias getAncestorPKs as 'Long[] getAncestorPKs(Long id) {
return new Long[]{new Long(1), new Long(2), new Long(3)};
}';
select * from accounts where id in
(select x from table(x bigint = getAncestorPKs(1)));

CAST error in a Where Clause

On the query below I keep getting this error:
Cannot read the next data row for the dataset DataSetProject. (rsErrorReadingNextDataRow)
It appears to be the where clause, if I take it out it seems to work. So I added a cast to the where clause with no luck. Is there something special I need to do in the where clause to get this to work? Just an FYI this is in a report that is pulling an id from the url.
SELECT new_projects.new_projectsId AS ProjectId
, new_projects.new_name AS ProjectName
, new_projects.new_Description AS ProjectDescription
FROM
new_projects
LEFT OUTER JOIN new_projectsteps
ON new_projects.new_projectsId = new_projectsteps.new_ProjectSteps2Id
LEFT OUTER JOIN Task
ON new_projectsteps.new_projectstepsId = Task.RegardingObjectId
WHERE
(new_projects.new_projectsId = cast(#id AS UNIQUEIDENTIFIER))
Thanks!
EDIT:
The id in SQL is a Unique Identifier, the value of #id is being pulled from the querystring(url). So it would look like: &id='BC02ABC0-A6A9-E111-BCAD-32B731EEDD84'
Sorry for the missing info.
I suspect the single quotes are coming through. So either don't have them there by stripping them out before being passed to your parameter or use:
WHERE new_projects.new_projectsId = CONVERT(UNIQUEIDENTIFIER, REPLACE(#id, '''', ''));
If you try a direct comparison when the GUID contains other characters, you should get:
Msg 8169, Level 16, State 2, Line 1 Conversion failed when
converting from a character string to uniqueidentifier.
If this is not what's happening, then don't say "the id in SQL is a Unique Identifier" - show ALL of the code so we can try to reproduce the problem.

Resources