I have a SQL query which takes user inputs hence security flaw is present.
The existing query is:
SELECT BUS_NM, STR_ADDR_1, CITY_NM, STATE_CD, POSTAL_CD, COUNTRY_CD,
BUS_PHONE_NB,PEG_ACCOUNT_ID, GDN_ALERT_ID, GBIN, GDN_MON_REF_NB,
ALERT_DT, ALERT_TYPE, ALERT_DESC,ALERT_PRIORITY
FROM ( SELECT A.BUS_NM, AE.STR_ADDR_1, A.CITY_NM, A.STATE_CD, A.POSTAL_CD,
CC.COUNTRY_CD, A.BUS_PHONE_NB, A.PEG_ACCOUNT_ID, 'I' ||
LPAD(INTL_ALERT_DTL_ID, 9,'0') GDN_ALERT_ID,
LPAD(IA.GBIN, 9,'0') GBIN, IA.GDN_MON_REF_NB,
DATE(IAD.ALERT_TS) ALERT_DT,
XMLCAST(XMLQUERY('$A/alertTypeConfig/biqCode/text()' passing
IAC.INTL_ALERT_TYPE_CONFIG as "A") AS CHAR(4)) ALERT_TYPE,
, ROW_NUMBER() OVER () AS "RN"
FROM ACCOUNT A, Other tables
WHERE IA.GDN_MON_REF_NB = '100'
AND A.PEG_ACCOUNT_ID = IAAR.PEG_ACCOUNT_ID
AND CC.COUNTRY_CD = A.COUNTRY_ISO3_CD
ORDER BY IA.INTL_ALERT_ID ASC )
WHERE ALERT_TYPE IN (" +TriggerType+ ");
I changed it to accept TriggerType from setString like:
SELECT BUS_NM, STR_ADDR_1, CITY_NM, STATE_CD, POSTAL_CD, COUNTRY_CD,
BUS_PHONE_NB,PEG_ACCOUNT_ID, GDN_ALERT_ID, GBIN, GDN_MON_REF_NB,
ALERT_DT, ALERT_TYPE, ALERT_DESC,ALERT_PRIORITY
FROM ( SELECT A.BUS_NM, AE.STR_ADDR_1, A.CITY_NM, A.STATE_CD, A.POSTAL_CD,
CC.COUNTRY_CD, A.BUS_PHONE_NB, A.PEG_ACCOUNT_ID,
'I' || LPAD(INTL_ALERT_DTL_ID, 9,'0') GDN_ALERT_ID,
LPAD(IA.GBIN, 9,'0') GBIN, IA.GDN_MON_REF_NB,
DATE(IAD.ALERT_TS) ALERT_DT,
XMLCAST(XMLQUERY('$A/alertTypeConfig/biqCode/text()' passing
IAC.INTL_ALERT_TYPE_CONFIG as "A") AS CHAR(4)) ALERT_TYPE,
ROW_NUMBER() OVER () AS "RN"
FROM ACCOUNT A, other tables
WHERE IA.GDN_MON_REF_NB = '100'
AND A.PEG_ACCOUNT_ID = IAAR.PEG_ACCOUNT_ID
AND CC.COUNTRY_CD = A.COUNTRY_ISO3_CD
ORDER BY IA.INTL_ALERT_ID ASC )
WHERE ALERT_TYPE IN (?);
Setting trigger type as below:
if (StringUtils.isNotBlank(request.getTriggerType())) {
preparedStatement.setString(1, triggerType != null ? triggerType.toString() : "");
}
Getting error as
Caused by: com.ibm.db2.jcc.am.SqlDataException: DB2 SQL Error: SQLCODE=-302, SQLSTATE=22001, SQLERRMC=null, DRIVER=4.19.26
The -302 SQLCODE indicates a conversion error of some sort.
SQLSTATE 22001 narrows that down a bit by telling us that you are trying to force a big string into a small variable. Given the limited information in your question, I am guessing it is the XMLCAST that is the culprit.
DB2 won't jam 30 pounds of crap into a 4 pound bag so to speak, it gives you an error. Maybe giving XML some extra room in the cast might be a help. If you need to make sure it ends up being only 4 characters long, you could explicitly do a LEFT(XMLCAST( ... AS VARCHAR(64)), 4). That way the XMLCAST has the space it needs, but you cut it back to fit your variable on the fetch.
The other thing could be that the variable being passed to the parameter marker is too long. DB2 will guess the type and length based on the length of ALERT_TYPE. Note that you can only pass a single value through a parameter marker. If you pass a comma separated list, it will not behave as expected (unless you expect ALERT_TYPE to also contain a comma separated list). If you are getting the comma separated list from a table, you can use a sub-select instead.
Wrong IN predicate use with a parameter.
Do not expect that IN ('AAAA, M250, ABCD') (as you try to do passing a comma-separated string as a single parameter) works as IN ('AAAA', 'M250', 'ABCD') (as you need). These predicates are not equivalent.
You need some "string tokenizer", if you want to pass such a comma-separated string like below.
select t.*
from
(
select XMLCAST(XMLQUERY('$A/alertTypeConfig/biqCode/text()' passing IAC.INTL_ALERT_TYPE_CONFIG as "A") AS CHAR(4)) ALERT_TYPE
from table(values xmlparse(document '<alertTypeConfig><biqCode>M250, really big code</biqCode></alertTypeConfig>')) IAC(INTL_ALERT_TYPE_CONFIG)
) t
--WHERE ALERT_TYPE IN ('AAAA, M250, ABCD')
join xmltable('for $id in tokenize($s, ",\s?") return <i>{string($id)}</i>'
passing cast('AAA, M250 , ABCD' as varchar(200)) as "s"
columns token varchar(200) path '.') x on x.token=t.ALERT_TYPE
;
Run the statement as is. Then you may uncomment the string with WHERE clause and comment out the rest to see what you try to do.
P.S.:
The error you get is probably because you don't specify the data type of the parameter (you don't use something like IN (cast(? as varchar(xxx))), and db2 compiler assumes that its length is equal to the length of the ALERT_TYPE expression (4 bytes).
I have the following code:
SELECT
ehrprg.ReportName
,ehrprg.AnnualGoalServiceMinutes
,COUNT(DISTINCT ct.[ClientFK]) AS [UnduplicatedClients]
FROM
[WH].[Fact].[EHRClinicalTransaction] ct
INNER JOIN [Dimension].EHRProgram ehrprg ON
ct.ProgramFK = ehrprg.WHID
WHERE
ehrprg.AnnualGoalServiceMinutes > 0
GROUP BY
ehrprg.ReportName
,ehrprg.AnnualGoalServiceMinutes
ORDER BY
ReportName
and result:
But I need it would have only one 'SM NV'(instead of 2) and one 'SM REACH' (instead of 3) rows in the [ReportName] column, summarizing [ServiceMinutes]
When I use SUM(ehrprg.AnnualGoalServiceMinutes) it gives me "Arithmetic overflow error converting to datatype int" error.
Then, I tried ,SUM(CONVERT(BIGINT, ehrprg.AnnualGoalServiceMinutes)), but getting the following:
It is still not grouping (not summarizing AnnualGoalServiceMinutes) and gives some values, I can't understand
My goal is to see instead of-
ReportName AnnualGoalServiceMin
SM NV 197885
SM NV 348654
SM REACH 40000
SM REACH 80000
SM REACH 380000
I expect the SUM of the AnnualGoalServiceMin:
ReportName AnnualGoalServiceMin
SM NV 546539
SN REACH 500000
Please, help
I don't know the source of the overflow error (it should not be happening based on the magnitude of the integers invovled), but I think what you need here is a second level of aggregation:
WITH cte AS (
SELECT
ehrprg.ReportName,
ehrprg.AnnualGoalServiceMinutes,
COUNT(DISTINCT ct.[ClientFK]) AS UnduplicatedClients
FROM [WH].[Fact].[EHRClinicalTransaction] ct
INNER JOIN [Dimension].EHRProgram ehrprg
ON ct.ProgramFK = ehrprg.WHID
WHERE ehrprg.AnnualGoalServiceMinutes > 0
GROUP BY ehrprg.ReportName, ehrprg.AnnualGoalServiceMinutes
)
SELECT
ReportName,
SUM(AnnualGoalServiceMinutes) AS AnnualGoalServiceMinutes,
SUM(UnduplicatedClients) AS UnduplicatedClients
FROM cte
GROUP BY
ReportName;
I am using below DB2 query to get the Specific Length of Character
from CLASSIFICATION Coulmn with Condition 0 before the '\', but I am getting error.
select SERVICE_REQUEST,
(case when
(SUBSTR(CLASSIFICATION,LOCATE('\',CLASSIFICATION)-2,1))='0' then
RIGHT(CLASSIFICATION,LENGTH(CLASSIFICATION)-LOCATE('\',CLASSIFICATION))
ELSE CLASSIFICATION
end)
as CLASSIFICATION2
from REPORTDB3.MAXIMO_INDIA_SR_MONTHLY_REPORT where length(SERVICE_REQUEST)=7
ERROR:
An error occurred while processing the results. -
The statement was not executed because a numeric argument of a scalar function is out of range.. SQLCODE=-138, SQLSTATE=22011, DRIVER=4.19.56
Can you please Help me on this.
Regards,
Sambit
Already Gone to Stackoverflow for the answers
I believe you should care about the situations, when the CLASSIFICATION column data have the following problems:
- No \ characters at all
- \ character in the 1-st 2 positions
Your query passes negative 2-nd parameter to the SUBSTR function in these cases, which is not allowed.
with REPORTDB3_MAXIMO_INDIA_SR_MONTHLY_REPORT(CLASSIFICATION, SERVICE_REQUEST) as (
values
('ab0c|def', '1234567')
, ('\def', '1234567')
, ('a\def', '1234567')
, ('ab0c\def', '1234567')
)
select SERVICE_REQUEST, CLASSIFICATION,
case SUBSTR(CLASSIFICATION, case when pos>2 then pos end -2, 1)
when '0' then SUBSTR(CLASSIFICATION, pos+1)
else CLASSIFICATION
end as CLASSIFICATION2
from (
select SERVICE_REQUEST, CLASSIFICATION, LOCATE('\', CLASSIFICATION) as pos
from REPORTDB3_MAXIMO_INDIA_SR_MONTHLY_REPORT
where length(SERVICE_REQUEST)=7
)
I'm getting a syntax error when trying to use the GROUPING SETS function in my Postgres DB.
I've looked at the documentation here and I believe the syntax is correct, but I'm still getting an error.
I believe the syntax should be GROUP BY GROUPING SETS ((COLUMN), (COLUMN), ()) to get the grand total of my SUM() from the the SELECT part of my code. So I put in GROUP BY GROUPING SETS ((amount.date), ( places.place), ());
What am I doing wrong here?
SELECT
EXTRACT(MONTH FROM amount.date),
places.place,
CASE
WHEN places.place = 'A' THEN SUM(amount.amount)
WHEN places.place = 'B' THEN SUM(amount.amount)
WHEN places.place = 'C' THEN SUM(amount.amount)
ELSE SUM((amount.amount) * 2.5)
END AS "Total"
FROM amount
LEFT JOIN places ON places.id = amount.id
WHERE EXTRACT(YEAR FROM amount.date) = 2017
GROUP BY GROUPING SETS ((amount.date), (places.place), ());
The code given below is a part of a view that I have created. But sometimes it may throw an error saying:
division by zero
The reason for this error is that sum(bills.past_arrear) part may be 0 for some months.
SELECT (SELECT revenue_driver.driver_id
FROM ccdb.revenue_driver
WHERE revenue_driver.driver_desc::text = 'Arrear Collection Efficiency'::text) AS driver_id
,bills.org_unit_id::integer AS section_id
,date_part('Month'::text, bills.due_date) AS mnth
,date_part('Year'::text, bills.due_date) AS yr
,ROUND(SUM(COALESCE(bills.arrear_collected,0::numeric))/sum(bills.past_arrear)*100::numeric, 2) AS per_efficiency
,now() AS creation_dt
FROM ccdb.bills
WHERE bills.due_date::date >= date_trunc('Month'::text,'now'::text::date::timestamp with time zone)::date
AND bills.due_date::date <= 'now'::text::date
AND (bills.bill_type_group_code::text = ANY (ARRAY['EB'::character varying::text, 'Energy'::character varying::text]))
GROUP BY bills.org_unit_id, date_part('Year'::text, bills.due_date), date_part('Month'::text, bills.due_date);
What I want is if ROUND(SUM(COALESCE(bills.arrear_collected,0::numeric))/sum(bills.past_arrear)*100::numeric, 2) throws division by zero error I want to replace the value with 0.
I have not idea how do handle this error. Kindly someone help me on this.
You need to use a CASE WHEN in your select statement like below :-
CASE
WHEN sum(bills.past_arrear) = 0
THEN 0
ELSE ROUND(SUM(COALESCE(bills.arrear_collected, 0::NUMERIC)) / sum(bills.past_arrear) * 10 0::NUMERIC, 2)
END AS per_efficiency