ORA-00909: invalid number of arguments when using NVL - sql

I have a column that gets the specific amount which has a condition like below.
I tried this one to get his specific value but I getting a multiple rows which it should be single row.
select distinct
(case
when aila.line_type_lookup_code = 'ITEM' and aila.tax_classification_code = 'VAT12 SERVICES' then to_char(aila.assessable_value) else '0'
end) as taxable_lines
from
ap_invoice_lines_all aila
where
aila.invoice_id = '31004'
then I tried this one to replace a null values.
select distinct
(case
when aila.line_type_lookup_code = 'ITEM' and aila.tax_classification_code = 'VAT12 SERVICES' then nvl(to_char(aila.assessable_value,0)) end) as taxable_lines
from
ap_invoice_lines_all aila
where
aila.invoice_id = '31004'
For example I have a table named ap_invoice_lines_all that has a columns name
line_type_lookup_code string
tax_classification_code string
assessable_value double
then the expected output I want based on the query I tried above is
taxable lines
1300
but the one I get is
taxable lines
0
1300
How do I remove the 0 in the result?
Thanks!

First off, you are using to_char wrong. This function should only have a single argument in this context:
nvl(to_char(aila.assessable_value,0))
should be
nvl(to_char(aila.assessable_value),0)
I would recommend against using a case expression like this in such a simple query to improve readability. Case expressions are great tools, but typically decrease the query readability. And in this particular instance, you don't really need an IF..THEN..ELSE function (which is the reason to use case expressions).
Secondly, are you sure there is only record where aila.invoice_id = '31004'? The query will only return two rows if there are actually two rows in the table where that clause is true. In this case it finds one row where assessable_value is null and one where it isn't.
In any case, to remove the zero (or in this case a null value) from the resultset, you can simply do this:
select distinct aila.assessable_value as taxable_lines
from ap_invoice_lines_all aila
where aila.invoice_id = '31004'
and aila.line_type_lookup_code = 'ITEM' -- Originally a condition in the case statement
and aila.tax_classification_code = 'VAT12 SERVICES' -- Originally a condition in the case statement
and aila.assessable_value is not null; -- Removes any null values from the result
Or if you want null values to be replaced by a zero;
select distinct nvl(aila.assessable_value, 0) as taxable_lines
from ap_invoice_lines_all aila
where aila.invoice_id = '31004'
and aila.line_type_lookup_code = 'ITEM' -- Originally a condition in the case statement
and aila.tax_classification_code = 'VAT12 SERVICES' -- Originally a condition in the case statement
Note that the distinct clause will cause all rows where assessable_value to be grouped into a single row if multiple rows are a possibility. Remove the distinct clause if you want all rows where assessable_value is null to show in the result as 0.
Lastly, you might want to think about implementing a primary key (if the table doesn't have one) or unique index on invoice_id, if there shouldn't ever be duplicate ids. And it's simply good practice to do so for ID columns which are used often in queries and should be unique.

Related

How to create an additional column in a SQL query that contains the number of rows with a column value equal to a column value from the current row?

This is what I currently have (it doesn't work):
select MOCKSTEMS.WORD_ID,
MOCKSTEMS.STEM_ID,
MOCKSTEMS.LABSTEM,
MOCKSTEMS.LABSTEMCATEGORY,
MOCKLEMMAS.LEMMAFORM,
MOCKSTEMS.LEMMA_ID,
MOCKWORDS.ORIGINALWORD,
MOCKSTEMS.CONTAINEDIN,
COUNT(*) as SAMEVALUE from MOCKSTEMS where CONTAINEDIN=STEM_ID
from MOCKSTEMS
inner join MOCKWORDS on MOCKSTEMS.WORD_ID = MOCKWORDS.WORD_ID
inner join MOCKLEMMAS on MOCKSTEMS.LEMMA_ID = MOCKLEMMAS.LEMMA_ID
Basically, I wish to create a column called 'SAMEVALUE' that shows the number of rows in this query with 'CONTAINEDIN' values equal to the 'STEM_ID' value of each row. Is this possible, and if so, how can I do it with SQL?
EDITED:
This is what I get when I run the query without the 'COUNT(*) as SAMEVALUE from MOCKSTEMS where CONTAINEDIN=STEM_ID' row:
image of a few rows returned by the query.
For example, for the row with STEM_ID='stem-003' and LABSTEM='owotan okitz', I would like the SAMEVALUE column to have value 2, because there are 2 rows with CONTAINEDIN='stem-003', as circled in this image.
It would also be fine if the SAMEVALUE column just indicates true/false (or 0/1) depending on whether there are rows with CONTAINEDIN values equal to the STEM_ID of each row.
To get overall count alongside the query results, you need an analytic function. So to count only rows with some condition, we put this condition in case expression, which returns something in case of "true", and null in other cases. Then count will ignore nulls.
select MOCKSTEMS.WORD_ID,
MOCKSTEMS.STEM_ID,
MOCKSTEMS.LABSTEM,
MOCKSTEMS.LABSTEMCATEGORY,
MOCKLEMMAS.LEMMAFORM,
MOCKSTEMS.LEMMA_ID,
MOCKWORDS.ORIGINALWORD,
MOCKSTEMS.CONTAINEDIN,
COUNT(
case
when CONTAINEDIN=STEM_ID
then 1
end
) over() as SAMEVALUE
/*Over is empty to consider all the result set as a single window*/
from MOCKSTEMS
inner join MOCKWORDS on MOCKSTEMS.WORD_ID = MOCKWORDS.WORD_ID
inner join MOCKLEMMAS on MOCKSTEMS.LEMMA_ID = MOCKLEMMAS.LEMMA_ID

Convert null values to zero in crosstab query

The code below is a subset of a larger code that results in a cross tab query. The issue is that the total column to the right which sums all the number columns in the cross tab query does not calculate accurately as it results a 0 for any rows that have null value in one of the twelve columns it is attempting to sum.
I believe that the i need to add a condition to the following line in the code to result in a zero if the value is null. I just need someone to take a second look at it. If there is a better alternate solution, I am happy to entertain that also.
SUM(entry) for MX in (M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12)
SELECT
tblCenters.prop
,tblAccounts.Category_EXCO
,tblCenters.CenterNum
,tblAccounts.AccountNum
,tblAccounts.Account_Description
,tblAccounts.Is_Revenue
,tblAccounts.Is_Above_EBITDA
,tblCenters.Division_Description
,tblCenters.[Is_F&B1]
,tblCenters.Group_Description
,Entry
,MX
FROM GA_Financial.dbo.tblSAP
left join tblMX on tblSAP.MDY = tblMX.MDY
left join tblAccounts on tblSAP.AccountNum = tblAccounts.AccountNum
left join tblCenters on tblSAP.CenterNum = tblCenters.CenterNum and tblSAP.Prop_SAP = tblCenters.PROP_SAP
WHERE tblAccounts.Is_Above_EBITDA = 1
AND tblSAP.Type = 'A'
)
AS Tab1
--The code below breaks down column "Entry" into twelve individual monthly columns and fills columns M1 through M12
PIVOT
(
SUM(entry) for MX in (M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12)
) as TAb2
where Prop = 'RWNY'
I think that you can use the COALESCE or IFNULL functions to replace the null value with a 0 for the sum calculation. Check out this response: COALESCE, IFNULL, or NZ() function that can be used in SQL Server and MS Access
To avoid a null result use ISNULL(Entry, 0) instead of Entry in your original query. This will substitute any returned null's with 0's.

PostgreSQL where clause not pushed down when using grouping sets

SELECT *
FROM (
SELECT SUM(quantity) AS quantity,
product_location_id,
location_bin_id,
product_lot_id,
product_serial_id,
CASE
WHEN GROUPING (product_location_id, location_bin_id, product_lot_id, product_serial_id) = 0 AND product_serial_id IS NOT NULL THEN
'Serial'
WHEN GROUPING (product_location_id, location_bin_id, product_lot_id, product_serial_id) = 0 THEN
'Lot'
ELSE
'Quantity'
END AS pick_by
FROM product_location_bins
WHERE status != 'Void'
AND has_quantity = 'Yes'
GROUP BY GROUPING SETS (
(product_location_id, location_bin_id, product_lot_id, product_serial_id),
(product_location_id, location_bin_id)
)
HAVING SUM(quantity) > 0
) x
WHERE x.product_serial_id = 5643
I have the above query. Using a normal GROUP BY postgres is able to "push down" the outer where clause and use the index on product_serial_id. When I use grouping sets it's unable to do so. It resolves the entire inner query and then filters the results. I'm wondering why this is. Is it a limitation with grouping sets?
Your query is odd. Your outer where clause eliminates the second set of results from grouping sets, because product_serial_id would be NULL for the second set. This gets filtered out in the outer where.
I think you want something like this for the outer query:
WHERE x.product_serial_id = 5643 OR x.product_serial_id IS NULL
I suppose that Postgres could add optimizations for poorly written code -- that is, eliminate the work for the second grouping sets set because it is filtered out by the outer where. However, that is not usually the focus of optimizations.

SSRS CountRows of a specific Field containing a specific Value

I am building an SSRS report. I have a DataSet that contains several Fields, one of which is Doc_Type. Doc_Type can contain several values, one of which is 'Shipment'.
In my report, I want to count the number of Doc_Types that are equal to 'Shipment'. This is what I am using, and it is not working:
=CountRows(Fields!Doc_Type.Value = "Shipments")
The error I get is: "The Value expression for the textrun 'Doc_Type.Paragraphs[0].TextRuns[0]' has a scope parameter that is not valid for an aggregate function. The scope parameter must be set to a string constant that is equal to either the name of a containing group, the name of a containing data region, or the name of a dataset.
You need to use an IIF to evaluate your fields and SUM the results. If your expression is in a table, you could use this:
=SUM(IIF(Fields!Doc_Type.Value = "Shipments", 1, 0))
There are many ways to achieve this.
Method 1
You can set up your expression something like this
=SUM(IIf(Fields!Doc_Type.Value = "Shipments", 1, 0), "YourDataSetName")
Remember SSRS is case sensitive so put your dataset name and Field names correctly.
Method 2
I prefer handling it in SQL as I want to keep the business logic out of RDLs.
You can use window functions to get the shipment count.
Also notice in the case of count I haven't added ELSE 0 condition. Because that will give wrong results. Count doesn't care what is the value inside it. It just counts all Non Null values. ELSE NULL will work.
SELECT Column1, Column2, Column3,
SUM(CASE WHEN Doc_Type = 'Shipments' THEN 1 ELSE 0 END) OVER() ShipmentCount_UsingSum
COUNT(CASE WHEN Doc_Type = 'Shipments' THEN 1 END) OVER() ShipmentCount_UsingCount
FROM myTable
JOIN....
WHERE .....
Now you can use this field in the report.

CASE Clause on select clause throwing 'SQLCODE=-811, SQLSTATE=21000' Error

This query is very well working in Oracle. But it is not working in DB2. It is throwing
DB2 SQL Error: SQLCODE=-811, SQLSTATE=21000, SQLERRMC=null, DRIVER=3.61.65
error when the sub query under THEN clause is returning 2 rows.
However, my question is why would it execute in the first place as my WHEN clause turns to be false always.
SELECT
CASE
WHEN (SELECT COUNT(1)
FROM STOP ST,
FACILITY FAC
WHERE ST.FACILITY_ID = FAC.FACILITY_ID
AND FAC.IS_DOCK_SCHED_FAC=1
AND ST.SHIPMENT_ID = 2779) = 1
THEN
(SELECT ST.FACILITY_ALIAS_ID
FROM STOP ST,
FACILITY FAC
WHERE ST.FACILITY_ID = FAC.FACILITY_ID
AND FAC.IS_DOCK_SCHED_FAC=1
AND ST.SHIPMENT_ID = 2779
)
ELSE NULL
END STAPPFAC
FROM SHIPMENT SHIPMENT
WHERE SHIPMENT.SHIPMENT_ID IN (2779);
The SQL standard does not require short cut evaluation (ie evaluation order of the parts of the CASE statement). Oracle chooses to specify shortcut evaluation, however DB2 seems to not do that.
Rewriting your query a little for DB2 (8.1+ only for FETCH in subqueries) should allow it to run (unsure if you need the added ORDER BY and don't have DB2 to test on at the moment)
SELECT
CASE
WHEN (SELECT COUNT(1)
FROM STOP ST,
FACILITY FAC
WHERE ST.FACILITY_ID = FAC.FACILITY_ID
AND FAC.IS_DOCK_SCHED_FAC=1
AND ST.SHIPMENT_ID = 2779) = 1
THEN
(SELECT ST.FACILITY_ALIAS_ID
FROM STOP ST,
FACILITY FAC
WHERE ST.FACILITY_ID = FAC.FACILITY_ID
AND FAC.IS_DOCK_SCHED_FAC=1
AND ST.SHIPMENT_ID = 2779
ORDER BY ST.SHIPMENT_ID
FETCH FIRST 1 ROWS ONLY
)
ELSE NULL
END STAPPFAC
FROM SHIPMENT SHIPMENT
WHERE SHIPMENT.SHIPMENT_ID IN (2779);
Hmm... you're running the same query twice. I get the feeling you're not thinking in sets (how SQL operates), but in a more procedural form (ie, how most common programming languages work). You probably want to rewrite this to take advantage of how RDBMSs are supposed to work:
SELECT Current_Stop.facility_alias_id
FROM SYSIBM/SYSDUMMY1
LEFT JOIN (SELECT MAX(Stop.facility_alias_id) AS facility_alias_id
FROM Stop
JOIN Facility
ON Facility.facility_id = Stop.facility_id
AND Facility.is_dock_sched_fac = 1
WHERE Stop.shipment_id = 2779
HAVING COUNT(*) = 1) Current_Stop
ON 1 = 1
(no sample data, so not tested. There's a couple of other ways to write this based on other needs)
This should work on all RDBMSs.
So what's going on here, why does this work? (And why did I remove the reference to Shipment?)
First, let's look at your query again:
CASE WHEN (SELECT COUNT(1)
FROM STOP ST, FACILITY FAC
WHERE ST.FACILITY_ID = FAC.FACILITY_ID
AND FAC.IS_DOCK_SCHED_FAC = 1
AND ST.SHIPMENT_ID = 2779) = 1
THEN (SELECT ST.FACILITY_ALIAS_ID
FROM STOP ST, FACILITY FAC
WHERE ST.FACILITY_ID = FAC.FACILITY_ID
AND FAC.IS_DOCK_SCHED_FAC = 1
AND ST.SHIPMENT_ID = 2779)
ELSE NULL END
(First off, stop using the implicit-join syntax - that is, comma-separated FROM clauses - always explicitly qualify your joins. For one thing, it's way too easy to miss a condition you should be joining on)
...from this it's obvious that your statement is the 'same' in both queries, and shows what you're attempting - if the dataset has one row, return it, otherwise the result should be null.
Enter the HAVING clause:
HAVING COUNT(*) = 1
This is essentially a WHERE clause for aggregates (functions like MAX(...), or here, COUNT(...)). This is useful when you want to make sure some aspect of the entire set matches a given criteria. Here, we want to make sure there's just one row, so using COUNT(*) = 1 as the condition is appropriate; if there's more (or less! could be 0 rows!) the set will be discarded/ignored.
Of course, using HAVING means we're using an aggregate, the usual rules apply: all columns must either be in a GROUP BY (which is actually an option in this case), or an aggregate function. Because we only want/expect one row, we can cheat a little, and just specify a simple MAX(...) to satisfy the parser.
At this point, the new subquery returns one row (containing one column) if there was only one row in the initial data, and no rows otherwise (this part is important). However, we actually need to return a row regardless.
FROM SYSIBM/SYSDUMMY1
This is a handy dummy table on all DB2 installations. It has one row, with a single column containing '1' (character '1', not numeric 1). We're actually interested in the fact that it has only one row...
LEFT JOIN (SELECT ... )
ON 1 = 1
A LEFT JOIN takes every row in the preceding set (all joined rows from the preceding tables), and multiplies it by every row in the next table reference, multiplying by 1 in the case that the set on the right (the new reference, our subquery) has no rows. (This is different from how a regular (INNER) JOIN works, which multiplies by 0 in the case that there is no row) Of course, we only maybe have 1 row, so there's only going to be a maximum of one result row. We're required to have an ON ... clause, but there's no data to actually correlate between the references, so a simple always-true condition is used.
To get our data, we just need to get the relevant column:
SELECT Current_Stop.facility_alias_id
... if there's the one row of data, it's returned. In the case that there is some other count of rows, the HAVING clause throws out the set, and the LEFT JOIN causes the column to be filled in with a null (no data) value.
So why did I remove the reference to Shipment? First off, you weren't using any data from the table - the only column in the result set was from the subquery. I also have good reason to believe that there would only be one row returned in this case - you're specifying a single shipment_id value (which implies you know it exists). If we don't need anything from the table (including the number of rows in that table), it's usually best to remove it from the statement: doing so can simplify the work the db needs to do.