SSRS is removing multiple lines in grouping - sql

I have an SSRS report with the following query:
SELECT DISTINCT
Rtrim(ltrim(CUSTNAME)) as 'CUSTNAME',
ItemName,
ISNULL(NAME, LOGCREATEDBY) AS 'Modified By'
,b.ITEMID as 'Item Id'
,[PRICE_NEW] as 'New Price'
,[PRICE_OLD] as 'Old Price'
,[PRICEUNIT_NEW] as 'New Unit Price'
,[PRICEUNIT_OLD] as 'Old Unit Price'
,LOGCREATEDDATE as 'Created Date'
,LOGCREATEDTIME
,(select Description from Dimensions where a.Dimension2_ = Dimensions.Num) as 'Division'
,(Select TOP 1 INVENTTRANS.DATEFINANCIAL From INVENTTRANS Where
INVENTTRANS.ITEMID = B.ITEMID and InvoiceID like 'Inv%' order by INVENTTRANS.DATEFINANCIAL desc) As 'LastInvoice'
FROM PMF_INVENTTABLEMODULELOG AS b
LEFT JOIN USERINFO ON ID = LOGCREATEDBY
LEFT JOIN INVENTTABLE AS a on a.ITEMID in (b.itemId)
WHERE LOGCREATEDDATE between #beginCreatedDate and #endCreatedDate
and a.dimension2_ in (#dimension)
order by LOGCREATEDDATE,LOGCREATEDTIME desc
What happens, in short, is it goes through a table and picks out an item number and lists each price change for that item.
the query, wen run, will return something like:
CUSTNAME | Modified By | Item ID | New Price | Old Price
------------------------------------------------------------------
Performance Joe 12345 21.50 21.49
Performance Mary 12345 21.49 19.10
(This happens to be the return that is causing problem)
My report lists each line by division, Customer name and item Number. The problem is, when I have an Item ID group, it adds up the total (makes sense) So i get rid of the item number group, but now it will list only one item per customer!
it should show the two lines for Performance in the example, but instead, it lists neither. I would like it to show every single line for each customer. It must be the ITEM ID group, but I can't seem to get it right.

Rather than getting rid of the group, change it to show detail data.
Right click on the group select 'Group Properties' and select the Group On expression. Then click the delete button. It will then no longer sum as it is a detail group.
I would recommend that you then remove sum from the relevant expressions, to avoid confusion, as they will only be summing single values but will make it look otherwise.

Related

SSRS Report Duplicates Flag

I'm presently making a report on ssrs from a Great Plains Dynamics DB.
It's a pretty simple report with a few columns. Everything works fine but in GP, when creating an Invoice or a Return bill, sometimes GP is gonna give the same Bill ID. It doesn't really shows as a duplicated because for GP, an Invoice and a Return can/could have the same ID because they are not the same type. Don't ask me why..
So for my report, when I start a research from a multiples values parameters with my SopNumber (Bill ID) it gives me the right information. But now I would like to have a flag on those information that equals the bill ID and has Invoice and Return at the same time.
Since it is normal for GP to have 2 different type of document for the same ID, I cannot ask my report to remove the return or the invoice cause in different case, an Invoice could be the important document and in an other case, the return.
In my tablix, I do not show the Bill ID, because I don't need the information except when there is this "duplication".
I would also like after the flag (line highlited), that on the top of my report, a sentence shows something like : "Those documents are in conflict : 000123123". So with this information showing my ID, I can now go in my multiple values parameter and remove this number.
I wanna achieve those 2 flags because I'm making a calcul from my documents amount and if those 2 would be there, it would make my calcul wrong.
I hope you guys can help me. If so, thank you very much in advance!
I worked on a couple of different expressions but never got the result I wanted. Use previous statement and equals but couldn't find how to make it look like is equals to Invoice and Return and SopNumber equals the same.
select CASE SOP10200.SOPTYPE
WHEN 1 THEN 'QUOTE'
WHEN 2 THEN 'ORDER'
WHEN 3 THEN 'INVOICE'
WHEN 4 THEN 'RETURN'
WHEN 5 THEN 'BACK ORDER'
WHEN 6 THEN 'FULLFILLMENT ORDER'
END AS SOPTYPE,
sop10200.SLPRSNID,
sop10200.XTNDPRCE as ExtendedPrice,
sop10200.SOPNUMBE,
iv00101.ITMCLSCD as FAMILYCLASS,
sop10100.DOCDATE
from sop10200
left join iv00101 on sop10200.ITEMNMBR = iv00101.ITEMNMBR
left join sop10100 on sop10200.SOPNUMBE = sop10100.SOPNUMBE
WHERE SOP10100.DOCDATE BETWEEN '2018-01-01 00:00:00.000' AND '2035-01-01 00:00:00.000'
union all
select CASE SOP30300.SOPTYPE
WHEN 1 THEN 'QUOTE'
WHEN 2 THEN 'ORDER'
WHEN 3 THEN 'INVOICE'
WHEN 4 THEN 'RETURN'
WHEN 5 THEN 'BACK ORDER'
WHEN 5 THEN 'FULLFILLMENT ORDER'
END AS SOPTYPE,
sop30300.SLPRSNID,
sop30300.XTNDPRCE as ExtendedPrice,
sop30300.SOPNUMBE,
iv00101.ITMCLSCD as FAMILYCLASS,
sop30200.DOCDATE
from sop30300
left join iv00101 on sop30300.ITEMNMBR = iv00101.ITEMNMBR
left join sop30200 on sop30300.SOPNUMBE = sop30200.SOPNUMBE
WHERE SOP30200.DOCDATE BETWEEN '2018-01-01 00:00:00.000' AND '2035-01-01 00:00:00.000'
ORDER BY SOPNUMBE desc
Correct me if I'm wrong, but it sounds like you want a way to see if there is a duplicate record for the report and use that in a calculation?
You could do a subquery to retrieve a count, and if that is more than 1 than you have a duplicate record
EDIT:
SELECT *, (IF
(SELECT Count(S1.SOPTYPE) FROM sop10200 S1 WHERE T1.BillID = S1.BillID ) > 1 THEN "Duplicate " ELSE "" END) AS DuplicateCheck
FROM sop10200 T1
The above solution will give you a value you can use on each page to report whether or not that it has a duplicate BillID.

SQL query not working properly (not sure about it)

I have 2 tables: ORDER_INFO_TABLE and APPROVE_TABLE
The first one contains the information related to "orders".. DO NOTE that a single order may contain different "order lines" (thats why you'll see the order number repeated in the image below).Also, there exist a field called "PRICE" (this field is not SHOWN in my image) and CANCELLED (order line status)
The second table contains the information related to the "approvers" for the different order lines. Inside this table you will see a field called "APPROVERID", ITEMID (order line id) and "APPROVED" (order line status). The approvers must check if the price is okay or not. If the order line is okay, the approver will put a number 1 in the field "APPROVED". If the price is not correct, he will put a number 1 in the field CANCELLED in the other table.
Take a look at these images:
I have tried without success to obtain all the cancelled orders (an order is cancelled when all ITS order lines are cancelled) AND the approved order (an approved order may contain cancelled order lines) for an specific approver
I tried so many times, using count operator, left join but i am completely lost :(
A guy suggested me to use this query:
SELECT web_order_id
FROM ORDER_INFO_TABLE oif
INNER JOIN APPROVE_TABLE apt
ON (oif.item_id = apt.item_id)
GROUP BY web_order_id
HAVING SUM(cancelled) = COUNT(*);
The problem is that it is not woking properly (it shows this error message: operand data type is invalid for sum operator) AND using this query i will not be able to obtain the CANCELLED and APPROVED orders for a specific approver.
EDIT:
SELECT web_order_id
FROM ORDER_INFO_TABLE oif
INNER JOIN APPROVE_TABLE apt
ON (oif.item_id = apt.item_id **and apt.APPROVERID = 'RANDOM#MAILNATOR.COM'**)
GROUP BY web_order_id
HAVING SUM(cancelled) = COUNT(*);
Edited: If you just want to select order from specific approver then just use simple select with join:
SELECT oif.web_order_id, oif.item_id, apt.approverid,
oif.cancelled, apt.approved
FROM ORDER_INFO_TABLE oif
INNER JOIN APPROVE_TABLE apt
ON oif.item_id = apt.item_id
WHERE apt.approverid = 'RANDOM#MAILNATOR.COM'
ORDER BY oif.web_order_id, oif.item_id;
Seems that your cancelled column datatype is VARCHAR. You should convert it to number before using SUM function
SELECT oif.web_order_id
FROM ORDER_INFO_TABLE oif
INNER JOIN APPROVE_TABLE apt
ON oif.item_id = apt.item_id
GROUP BY oif.web_order_id
HAVING SUM(CAST(oif.cancelled AS int)) = COUNT(*);

Flatten multiple query results with same ID to single row?

I'm curious about something in a SQL Server database. My current query pulls data about my employer's items for sale. It finds information for just under 105,000 items, which is correct. However, it returns over 155,000 rows, because each item has other things related to it. Right now, I run that data through a loop in Python, manually flattening it out by checking if the item the loop is working on is the same one it just worked on. If it is, I start filling in that item's extra information. Ideally, the SQL would return all this data already put into one row.
Here is an overview of the setup. I'm leaving out a few details for simplicity's sake, since I'm curious about the general theory, not looking for something I can copy and paste.
Item: contains the item ID, SKU, description, vendor ID, weight, and dimensions.
AttributeName: contains attr_id and attr_text. For instance, "color", "size", or "style".
AttributeValue: contains attr_value_id and attr_text. For instance, "blue" or "small".
AttributeAssign: contains item_id and attr_id. This ties attribute names to items.
attributeValueAssign: contains item_id and attr_value_id, tying attribute values to items.
A series of attachments is set up in a similar way, but with attachment and attachmentAssignment. Attachments can have only values, no names, so there is no need for the extra complexity of a third table as there is with attributes.
Vendor is simple: the ID is used in the item table. That is:
select item_id, vendorName
from item
join vendor on vendor_id = item.vendorNumber
gets you the name of an item's vendor.
Now, the fun part: items may or may not have vendors, attributes, or attachments. If they have either of the latter two, there's no way to know how many they have. I've seen items with 0 attributes and items with 5. Attachments are simpler, as there can only be 0 or 1 per item, but the possibility of 0 still demands an outer left join so I am guaranteed to get all the items.
That's how I get multiple rows per item. If an item has three attrigbutes, I get either four or seven rows for just that item--I'm not sure if it's a row per name/value or a row per name AND a row per value. Either way, this is the kind of thing I'd like to stop. I want each row in my result set to contain all attributes, with a cap at seven and null for any missing attribute. That is, something like:
item_id; item_title; item_sku; ... attribute1_name; attribute1_value; attribute2_name; attribute2_value; ... attribute7_value
1; some random item; 123-45; ... color; blue; size; medium; ... null
Right now, I'd get multiple rows for that, such as (only ID and attributes):
ID; attribute 1 name; attribute 1 value; attribute 2 name; attribute 2 value
1; color; blue; null; null
1; color; blue; size; medium
I'm after the second row only--all the information put together into one row per unique item ID. Currently, though, I get multiple rows, and Python has to put everything together. I'm outputting this to a spreadsheet, so information about an item has to be on that item's row.
I can just keep using Python if this is too much bother. But I wondered if there was a way to do it that would be relatively easy. My script works fine, and execution time isn't a concern. This is more for my own curiosity than a need to get anything working. Any thoughts on how--or if--this is possible?
Here is #WCWedin's answer modified to use a CTE.
WITH attrib_rn as
(
select
*, row_number() over(partition by item_id order by name, attribute_id) as row_number
from attributes
)
select
i.item_id,
attr1.name as attribute1_name, attr1.value as attribute1_value,
...
attr7.name as attribute7_name, attr7.value as attribute7_value
from items i
left join attrib_rn as attr1 ON attr1.item_id = i.item_id AND attr1.row_number = 1
left join attrib_rn as attr2 ON attr2.item_id = i.item_id AND attr2.row_number = 2
left join attrib_rn as attr3 ON attr3.item_id = i.item_id AND attr3.row_number = 3
left join attrib_rn as attr4 ON attr4.item_id = i.item_id AND attr4.row_number = 4
left join attrib_rn as attr5 ON attr5.item_id = i.item_id AND attr5.row_number = 5
left join attrib_rn as attr6 ON attr6.item_id = i.item_id AND attr6.row_number = 6
left join attrib_rn as attr7 ON attr7.item_id = i.item_id AND attr7.row_number = 7
Since you only want the first 7 attributes and you want to keep all of the logic in the SQL query, you're probably looking at using row_number. Subqueries will do the job directly with multiple joins, and the performance will probably be pretty good since you're only joining so many times.
select
i.item_id,
attr1.name as attribute1_name, attr1.value as attribute1_value,
...
attr7.name as attribute7_name, attr7.value as attribute7_value
from
items i
left join (
select
*, row_number() over(partition by item_id order by name, attribute_id) as row_number
from
attributes
) AS attr1 ON
attr1.item_id = i.item_id
AND attr1.row_number = 1
...
left join (
select
*, row_number() over(partition by item_id order by name, attribute_id) as row_number
from
attributes
) AS attr7 ON
attr7.item_id = i.item_id
AND attr7.row_number = 7
In SQL Server, you can tackle this with a subquery containing 'ROW_NUMBER() OVER', and a few CASE statements to map the top 7 into columns.
A little tricky, but post your full query that returns the big list and I'll demonstrate how to transpose it.

Using the HAVING Clause with GROUP by to Return Unique Records

Good day all,
I am having difficulty understanding the mechanics of the GROUP BY AND HAVING clause and was hoping for some advice.
I am trying to query two tables - PRODUCTS and ORDER_ITEMS. The PRODUCT_ID column is used to link these two tables.
I wish to view products which have been ordered from a certain supplier (filtered using the SUPPLIER_ID column which is in ORDER_ITEMS); have been successfully ordered before (ORDER_STATUS 6 in ORDER_ITEMS);and which have not been deleted (RECORD_DELETED column in ORDER_ITEMS). I only use the PRODUCTS table to show the name of the product. Furthermore I only want distinct products returned, meaning I want to exclude any results which duplicate the PRODUCT_ID column
This is the query that I am using:
SELECT
PD.PRODUCT_ID,
PD.PRODUCT_NAME,
PD.BARCODE,
PD.SUPPLIER_BARCODE,
COUNT(PD.PRODUCT_ID) AS COUNTED,
ODI.ORDER_ITEM_ID
FROM PRODUCTS PD
INNER JOIN ORDER_ITEMS ODI
ON PD.PRODUCT_ID = ODI.PRODUCT_ID
WHERE ODI.SUPPLIER_ID = 34359738399
AND ORDER_STATUS = 6
AND ODI.RECORD_DELETED = 0
GROUP BY PD.PRODUCT_ID,PD.PRODUCT_NAME,PD.BARCODE,PD.SUPPLIER_BARCODE,ODI.ORDER_ITEM_ID
HAVING COUNT(ODI.PRODUCT_ID) = 1
ORDER BY PRODUCT_ID ASC
Unfortunately this is returning 502 records with many of them duplicating the PRODUCT_ID. If I remove the ORDER_ITEM_ID column from the query 175 records are returned. These 175 records are products that meet the criteria given above. The problem is that I also need to pull the ORDER_ITEM_ID from ORDER_ITEMS (along with some other columns).
I vaguely understand that when I include ORDER_ITEMS the query is going to group the data by the ORDER_ITEM column and so will count the PRODUCT_ID values based on each individual ORDER_ITEM_ID. This results in there always being a count of 1 for each product.
How does one get around this? Also, is there a more suitable way of carrying out this task which would allow me to include one ORDER_ITEM record for every duplicated product? Rather than omitting them altogether as I am doing above?
This is some of the data that is returned by the query above:
PRODUCT_ID,PRODUCT_NAME,BARCODE,SUPPLIER_BARCODE,COUNTED,ORDER_ITEM_ID
34359738628,ADCORTYL INTRA-ARTIC/DERMAL 10MG/ML 5ML,5099627022132,5012712000037,1,34359755708
34359739609,ARTELAC 3.2MG/ML EYE DROPS SOLN,5099627456722,5027519008933,1,34359741719
34359739626,ASACOLON 500MG SUPPOSITORIES,5099627516587,5015313012737,1,34359742783
34359739767,ATROVENT 250MCG/1ML UDV NEB SOLN,5099627639637,5012816012561,1,34359738421
34359739770,ATROVENT 500MCG/2ML UDV NEB SOLN,5099627460293,5012816012592,1,34359743524
34359739893,AZOPT 10MG/ML EYE DROPS SUSP,5099627831543,5015664002753,1,34359749091
34359739893,AZOPT 10MG/ML EYE DROPS SUSP,5099627831543,5015664002753,1,34359749687
34359739893,AZOPT 10MG/ML EYE DROPS SUSP,5099627831543,5015664002753,1,34359749715
34359739893,AZOPT 10MG/ML EYE DROPS SUSP,5099627831543,5015664002753,1,34359754053
34359740053,BACTIGRAS MED DRSS 10CMX10CM STERILE GMS,5099627672368,5000223421984,1,34359748101
34359740062,BACTROBAN 2% OINTMENT,5099627053914,5099211003165,1,34359755226
34359740558,BETNOVATE RD CREAM,5099627005692,5099211001642,1,34359752422
34359740558,BETNOVATE RD CREAM,5099627005692,5099211001642,1,34359738487
34359741045,BISODOL ANTACID TABS,5099627057707,5014398001438,1,34359750542
34359741995,BROLENE 0.1% EYE DROPS SOLN,5099627006323,50982790,1,34359746555
34359741995,BROLENE 0.1% EYE DROPS SOLN,5099627006323,50982790,1,34359751650
34359741995,BROLENE 0.1% EYE DROPS SOLN,5099627006323,50982790,1,34359751783
34359742132,BURINEX 1MG TABS,5099627551328,5702191004212,1,34359749705
34359742152,BUSCOPAN 20MG/ML SOLN FOR INJ,5099627006620,5012816018532,1,34359749083
In the example above, several records were returned with duplicate PRODUCT_ID values e.g ASACOLON 500MG SUPPOSITORIES
You need GROUP_CONCAT/LISTAGG equivalent in SQL Server. You can use XML, STUFF and correlated subquery as replacement.
If PRODUCT_ID is UNIQUE you can use:
WITH cte AS
(
SELECT
PD.PRODUCT_ID,
PD.PRODUCT_NAME,
PD.BARCODE,
PD.SUPPLIER_BARCODE,
ODI.ORDER_ITEM_ID
FROM PRODUCTS PD
JOIN ORDER_ITEMS ODI
ON PD.PRODUCT_ID = ODI.PRODUCT_ID
WHERE ODI.SUPPLIER_ID = 34359738399
AND ORDER_STATUS = 6
AND ODI.RECORD_DELETED = 0
)
SELECT PRODUCT_ID,
PRODUCT_NAME,
BARCODE,
SUPPLIER_BARCODE,
[COUNTED] = COUNT(PD.PRODUCT_ID),
[ORDER_ITEM_ID] = STUFF((SELECT CONCAT(',' , ORDER_ITEM_ID)
FROM cte c2
WHERE c2.PRODUCT_ID = c1.PRODUCT_ID
ORDER BY c2.ORDER_ITEM_ID
FOR XML PATH ('')), 1, 1, '')
FROM cte c1
GROUP BY PRODUCT_ID,PRODUCT_NAME,BARCODE,SUPPLIER_BARCODE
HAVING COUNT(PRODUCT_ID) = 1
ORDER BY PRODUCT_ID ASC;
LiveDemo_SimplifiedVersion
Otherwise correlate using multiple columns:
SELECT CONCAT(',' , ORDER_ITEM_ID)
FROM cte c2
WHERE c2.PRODUCT_ID = c1.PRODUCT_ID
AND c2.PRODUCT_NAME = c1.PRODUCT_NAME
AND ...
ORDER BY c2.ORDER_ITEM_ID
FOR XML PATH ('')), 1, 1, '')

SQL Query: How to list joined values on the same row

I've got a table of job listings and a related table which contains the job listing ids and each of the field values in a 'field title' -> 'field value' format.
So to get my listing of jobs, I've JOINED the tables in my SQL Query, but am getting the results on multiple lines because of that. Let me illustrate.
Query is something like:
SELECT list.id, list.activation_date, list_field.value
FROM listings AS list
INNER JOIN listings_fields AS list_field ON list.id = list_field.id
WHERE list.activation_date > SOME VALUE
AND list_field.field_id IN ('Title', 'Category')
ORDER BY list.activation_date DESC, list_field.field_id DESC
The result looks like this:
51325 2012-07-31 Job Title 1
51325 2012-07-31 Category 1, Category 2
51324 2012-07-31 Job Title 2
51324 2012-07-31 Category 3
51323 2012-07-31 Job Title 3
51323 2012-07-31 Category 1, Category 3
I've got all the data I need, it's ordered consistently with title first and category second, but I can't think of how to get the result all in one row. This must be a common problem with a well-known trick, and I'm sorry I don't know it yet.
Still learning. If anyone can help, I'd really appreciate it. :-)
Something like
SELECT list.id, list.activation_date, list_fieldJ.value,list_fieldC.value
FROM listings AS list
INNER JOIN listings_fields AS list_fieldJ ON list.id = list_fieldJ.id
and List_fieldj = 'Title'
INNER JOIN listings_fields AS list_fieldC ON list.id = list_fieldC.id
and List_fieldC = 'Category'
WHERE list.activation_date > SOME VALUE
ORDER BY list.activation_date DESC
Basically join once for title and then again for Category. Needless to say it's a pain if you have more than few value types to get out. And the above assumes that a job will always have title and category. Optional ones you'd need an outer join on.