SQL Subquery with Conditional Modulo - sql

I've put together this query and I am struggling to see why it is returning the full quantity of LINES rather than just the LINES that have an odd number of ITEMS. Ex: right now I have 2 LINES where COMPLETED is marked True. One of them has an odd number of ITEMS and one of them has an even number of ITEMS but the query is still returning 2 despite my conditional. What's wrong with my query?
SET #linesCompleted = (SELECT Count([so_line_number]) AS LINES
FROM [aof_order_line_queue]
WHERE [completed] = 'True'
AND (SELECT Count([serial_number]) AS ITEMS
FROM [aof_order_items]
WHERE so_line_number IN (SELECT
[so_line_number]
FROM
[aof_boxes_lines]
WHERE
[aof_boxes_id] = 880)) % 2 = 1)
The goal here is to compute the quantity of LINES that a box contains that are an odd number.
I suspect the problem could the IN subquery returning two results, though one result should be odd and one result should be even.

The problem is the lack of correlation in the outer query to the inner query.
You are asking it to count the number of lines in aof_order_line_queue where (something that's not related to the line) is true. So the thing in the parenthesis is either true or false for the whole data set, not for each line. If it's true, it will count all the lines. If it's false, it will return 0.
You have to add something like
AND aof_order_items.queue_id = aof_order_line_queue.queue_id
to your inner query.

Related

How to resolve a Left Join sub-query error?

In MS-Access I'm trying to join three tables. The third table is created from a sub-query designed to aggregate dates because I don't want multiple records per day when aligned with the first table.
When I entered the left join sub-query, I got this error:
The field is too small to accept the amount of data you attempted to
add. Try inserting or pasting less data.
I've run the sub-query separately and it returns about 19,000 records. Which is quite smaller than the actual table. If I use the actual table, the query works just fine, but it includes the duplicate records when there is more than one entry per day on the third table.
SELECT
SUM([ACD Calls]),
(SUM([Avg ACD Time]*[ACD Calls])/SUM([ACD Calls]))/86400,
(SUM([Avg ACW Time]*[ACD Calls])/SUM([ACD Calls]))/86400,
((SUM([Hold Time])/SUM([ACD Calls])))/86400,
((SUM([Avg ACD Time]*[ACD Calls])
+ SUM([Avg ACW Time]*[ACD Calls]))/SUM([ACD Calls]))/86400,
SUM([Time Adhering])/SUM([Total Time Scheduled]),
SUM([SS])/SUM([SO])
FROM
(
(
[GroupSumDaily]
LEFT JOIN Adherence_WKLY ON (GroupSumDaily.[Day] = Adherence_WKLY.[Day])
AND (GroupSumDaily.Agent = Adherence_WKLY.Agent)
)
LEFT JOIN
(
SELECT Evaluation_List.[Agent],
Evaluation_List.Recording_Date,
SUM(Evaluation_List.[Score]) as SS,
SUM(Evaluation_List.[Out of]) as SO
From Evaluation_List
Group By Evaluation_List.[Recording_Date],
Evaluation_list.[Agent]
)
as Evals ON (GroupSumDaily.[Day] = Evals.[Recording_Date])
AND (GroupSumDaily.Agent = Evals.Agent)
)
WHERE
(
[GroupSumDaily].[Agent] = "LastName FirstName"
AND Month([GroupSumDaily].[Day]) =1
AND Year([GroupSumDaily].[Day]) =2018
AND [GroupSumDaily].[Day] > #2/23/2015#
)
It looks like you don't have a "main" table to query from.
I'd try removing the first two open brackets after the FROM statement (and their equivalent closing brackets.)
If that doesn't fix it, try moving the whole sub-query into a separate query and selecting from the results...
It turns out the subquery fields are automatically limited to 50 characters and this was the root of the problem. When I limited the return to LEFT([Agent], 50), the error disappeared. Is there a way to set character length is a subquery field?
The other odd things is, none of my fields were actually over 50 characters... when I ran Select [Agent] Where LEN([Agent]) >= 50, it returned only 1 records, and it was the "NEW" blank record from the bottom. I confirmed that it completely blank, with no spaces or tabs. Very confusing.

Failed UPDATE with CASE

I'm trying to write a query which will update reorder_level based on how much of an item was sold within a particular time period.
with a as (select invoice_itemized.itemnum, inventory.itemname,
sum(invoice_itemized.quantity) as sold
from invoice_itemized
join inventory on invoice_itemized.itemnum=inventory.itemnum and
inventory.vendor_number='COR' and inventory.dept_id='cigs'
join invoice_totals on
invoice_itemized.invoice_number=invoice_totals.invoice_number and
invoice_totals.datetime>=dateadd(month,-1,getdate())
group by invoice_itemized.itemnum, inventory.itemname)
update inventory
set reorder_level = case when a.sold/numpervencase>=5 then 30
when a.sold/numpervencase>=2 then 20
when a.sold/numpervencase>=1 then 5
else 1 end,
reorder_quantity = 1
from a
join inventory_vendors on a.itemnum=inventory_vendors.itemnum
Replacing the update with a select performs entirely as expected, returning proper results from the case and selecting 94 rows.
with the update in place, all of the areas affected by the update (6758) got set to 1.
Run this, and eyeball the results:
with a as (select invoice_itemized.itemnum, inventory.itemname,
sum(invoice_itemized.quantity) as sold
from invoice_itemized
join inventory on invoice_itemized.itemnum=inventory.itemnum and
inventory.vendor_number='COR' and inventory.dept_id='cigs'
join invoice_totals on
invoice_itemized.invoice_number=invoice_totals.invoice_number and
invoice_totals.datetime>=dateadd(month,-1,getdate())
group by invoice_itemized.itemnum, inventory.itemname)
select a.sold, numpervencase, a.sold/numpervencase,
case
when a.sold/numpervencase>=5 then 30
when a.sold/numpervencase>=2 then 20
when a.sold/numpervencase>=1 then 5
else 1
end,
*
from a
join inventory_vendors on a.itemnum=inventory_vendors.itemnum
Always a good idea to select before update to check that data ends up as you expect
all of the areas affected by the update got set to 1
I put the raw ingredients into the query above; see if the sums worked out as expected. You might need to cast one of the operands to something with decimal places:
1/2 = 0
1.0/2 = 0.5
And it updated far more rows than i was expecting
Every row that comes out of that select will be updated. Identify the rows you don't want to update and put a where clause in to remove them
Am i overthinking this?
Undertesting, probably
Do I even need the cte?
Makes it easier to represent, but no- you could get the same result by pasting the contents of the cte in as a subquery.. it's what the db does (effectively) anyway
Do i have my from statement in the wrong place?
We don't know what result you're after so that one is impossible to answe beyond "doing so would probably generate a syntax error, so.. no"
The actual problem seems to be
your case when is always going to ELSE, find out why
your cte selects too many rows (I couldn't tell if the number you posted was the number you got or the number you were expecting but it's pretty moot without example data), find out why
Solved. When I added another join to the update it worked correctly. i had to add join inventory on inventory_vendors.itemnum=inventory.itemnum

Selecting only such groups that contain certain value

First of all, even though this SQL: How do you select only groups that do not contain a certain value? thread is almost identical to my problem, it doesn't fully dissipate my confusion about the problem.
Let's have a table "Contacts" like this one:
+----------------------+
| Department FirstName |
+----------------------+
| 100 Thomas |
| 200 Peter |
| 100 Jerry |
+----------------------+
First, I want to group the rows by the department number and show number of rows in each displayed group. This, I believe, can be easily done by the following query.
SELECT Department, Count(*) As "Rows_in_group"
FROM Contacts
GROUP BY Department
This outputs 2 groups. First with dep.no. 100 containing 2 rows, second with 200 containing only one row.
But then, I want to extend the query to exclude any group that doesn't contain certain value in certain column (e.g. Thomas in FirstName). Here are my questions:
1) Reading the above-mentioned thread I was able to come up with this, which seems to work correctly:
SELECT Department, Count(*) As "Rows_in_group"
FROM Contacts
WHERE Department IN (SELECT Department FROM Contacts WHERE FirstName = "Thomas")
GROUP BY Department
Q: How does this work? I understand the "WHERE Department IN" part, but then I'd expect a value, but instead another nested query is included, which to me doesn't make much sense as I'm only beginner with SQL.
2) By accident I was able to come up with another query that also seems to work, but feels weird and I also don't understand its workings.
SELECT Department, Count(*) As "Rows_in_group"
FROM Contacts
GROUP BY Department
HAVING NOT SUM(FirstName = "Thomas") = 0
Q: How does this work? Why alteration: HAVING SUM(FirstName = "Thomas") > 0 doesn't work?
3) Q: Is there any simple and correct way to do this using the HAVING clause?
I expected, that simple "HAVING FirstName='Thomas'" after the GROUP BY would do the trick as it seems to follow a common language, but it does not.
Note that I want the whole groups to be chosen by the query so "WHERE FirstName='Thomas'" isn't s solution for my problem as it excludes all the rows that don't satisfy the condition before the grouping takes place (at least the way I understand it).
Q: How does this work? I understand the "WHERE Department IN" part,
but then I'd expect a value, but instead another nested query is
included, which to me doesn't make much sense as I'm only beginner
with SQL.
The nested query returns values which are used to match against Department
2) By accident I was able to come up with another query that also
seems to work, but feels weird and I also don't understand its
workings.
HAVING NOT SUM(FirstName = "Thomas") = 0
"Feels weird" because, well, it is. This is not a place for the SUM function.
EDIT: Why does this work?
The expression FirstName = "Thomas" gets evaluated as true or false (known as a Boolean expression). True numerically is equal to 1 and False converts to 0 (zero). By including SUM you then calculated the totals so really zero (still) means false and "not zero" is true. Then to make it weird(er) you included NOT which negated the whole thing and it becomes NOT TRUE = 0 or FALSE = FALSE (which is of course... TRUE)!!
EDIT: I think what could be more helpful to you is consideration of when to use WHERE and when to use HAVING (instead of the Boolean magic taking place).
From this answer:
WHERE clause introduces a condition on individual rows; HAVING clause introduces a condition on aggregations, i.e. results of selection where a single result, such as count, average, min, max, or sum, has been produced from multiple rows.
WHERE was appropriate for your example because first you want to "only return rows WHERE Department IN (100)" and then you want to "group those rows by Department" and get a COUNT of how many rows had been selected.

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.

How to use multiple conditions (With AND) in IIF expressions in ssrs

I want to hide rows in SSRS report having Zero Quantity.
There are following multiple Quantity Columns like Opening Stock, Gross Dispatched,Transfer Out, Qty Sold, Stock Adjustment and Closing Stock etc.
I am doing this task by using following expression:
=IIF(Fields!OpeningStock.Value=0 AND Fields!GrossDispatched.Value=0 AND
Fields!TransferOutToMW.Value=0 AND Fields!TransferOutToDW.Value=0 AND
Fields!TransferOutToOW.Value=0 AND Fields!NetDispatched.Value=0 AND Fields!QtySold.Value=0
AND Fields!StockAdjustment.Value=0 AND Fields!ClosingStock.Value=0,True,False)
But by using this expression in row visibility, report hides all the rows except Totals Row. Even though report should show rows having Quantities of above mentioned columns.
Total values are shown correct.
Note: I set this row visibility expression on Detail Row.
Without using expression result is as following.
For the first 2 rows all the quantities are 0 (ZERO), i want to hide these 2 rows.
How can I fix this problem, or which expression must I use to get required results?
Could you try this out?
=IIF((Fields!OpeningStock.Value=0) AND (Fields!GrossDispatched.Value=0) AND
(Fields!TransferOutToMW.Value=0) AND (Fields!TransferOutToDW.Value=0) AND
(Fields!TransferOutToOW.Value=0) AND (Fields!NetDispatched.Value=0) AND (Fields!QtySold.Value=0)
AND (Fields!StockAdjustment.Value=0) AND (Fields!ClosingStock.Value=0),True,False)
Note: Setting Hidden to False will make the row visible
You don't need an IIF() at all here. The comparisons return true or false anyway.
Also, since this row visibility is on a group row, make sure you use the same aggregate function on the fields as you use in the fields in the row. So if your group row shows sums, then you'd put this in the Hidden property.
=Sum(Fields!OpeningStock.Value) = 0 And
Sum(Fields!GrossDispatched.Value) = 0 And
Sum(Fields!TransferOutToMW.Value) = 0 And
Sum(Fields!TransferOutToDW.Value) = 0 And
Sum(Fields!TransferOutToOW.Value) = 0 And
Sum(Fields!NetDispatched.Value) = 0 And
Sum(Fields!QtySold.Value) = 0 And
Sum(Fields!StockAdjustment.Value) = 0 And
Sum(Fields!ClosingStock.Value) = 0
But with the above version, if one record has value 1 and one has value -1 and all others are zero then sum is also zero and the row could be hidden. If that's not what you want you could write a more complex expression:
=Sum(
IIF(
Fields!OpeningStock.Value=0 AND
Fields!GrossDispatched.Value=0 AND
Fields!TransferOutToMW.Value=0 AND
Fields!TransferOutToDW.Value=0 AND
Fields!TransferOutToOW.Value=0 AND
Fields!NetDispatched.Value=0 AND
Fields!QtySold.Value=0 AND
Fields!StockAdjustment.Value=0 AND
Fields!ClosingStock.Value=0,
0,
1
)
) = 0
This is essentially a fancy way of counting the number of rows in which any field is not zero. If every field is zero for every row in the group then the expression returns true and the row is hidden.
Here is an example that should give you some idea..
=IIF(First(Fields!Gender.Value,"vw_BrgyClearanceNew")="Female" and
(First(Fields!CivilStatus.Value,"vw_BrgyClearanceNew")="Married"),false,true)
I think you have to identify the datasource name or the table name where your data is coming from.