Question for SQL Server experts. In the below query I would like to have an additional column which also SUMs Quantity but does so based on a different Requirement Type. I have tried a few ideas - CASE and adding a subquery in the select list but all return far too many results. What I would like to see is MATERIAL, MATERIAL_DESCRIPTION,SIZE_LITERAL,SUM OF QUANTITY WHEN REQUIREMENT TYPE = 'PB', SUM OF QUANTITY WHEN REQUIREMENT TYPE = '01' Not sure how to add the quantity twice on two different conditions. Thanks in advance
SELECT MATERIAL,
MATERIAL_DESCRIPTION,
SIZE_LITERAL,
SUM(QUANTITY) AS 'SUM_FCST'
FROM VW_MRP_ALLOCATION
WHERE REQUIREMENT_CATEGORY = 'A60381002'
AND MATERIAL_AVAILABILITY_DATE >= GETDATE()
AND REQUIREMENT_TYPE ='PB'
GROUP BY MATERIAL,
MATERIAL_DESCRIPTION,
SIZE_LITERAL
ORDER BY MATERIAL,
SIZE_LITERAL
You would use a CASE expression inside of the SUM(). This is conditional aggregation:
SELECT MATERIAL,
MATERIAL_DESCRIPTION,
SIZE_LITERAL,
SUM(case when REQUIREMENT_TYPE ='PB' then QUANTITY else 0 end) AS 'SUM_FCST_PB',
SUM(case when REQUIREMENT_TYPE ='01' then QUANTITY else 0 end) AS 'SUM_FCST_01'
FROM VW_MRP_ALLOCATION
WHERE REQUIREMENT_CATEGORY = 'A60381002'
AND MATERIAL_AVAILABILITY_DATE >= GETDATE()
GROUP BY MATERIAL,
MATERIAL_DESCRIPTION,
SIZE_LITERAL
ORDER BY MATERIAL,
SIZE_LITERAL
You can use case inside sum as below:
SELECT MATERIAL,
MATERIAL_DESCRIPTION,
SIZE_LITERAL,
SUM(CASE WHEN [Requirement_Type] = 'PB' then QUANTITY else 0 end) AS 'SUM_FCST'
FROM VW_MRP_ALLOCATION
WHERE REQUIREMENT_CATEGORY = 'A60381002'
AND MATERIAL_AVAILABILITY_DATE >= GETDATE()
AND REQUIREMENT_TYPE ='PB'
GROUP BY MATERIAL,
MATERIAL_DESCRIPTION,
SIZE_LITERAL
ORDER BY MATERIAL,
SIZE_LITERAL
Related
I have data like in the below format
I want output in the below format
Please help me with the SQL code. Thanks !
Like I mention in the comments, you need to fix whatever it is that's inserting the data and not lose the values so that they become NULL in "newer" rows.
To get the results you want, you'll going to have to use row numbering and conditional aggregation, which is going to get messy the more columns you have; and why you need to fix the real problem. This will look something like this:
WITH CTE AS(
SELECT GroupingColumn,
NullableCol1,
NullableCol2,
DateColumn,
CASE WHEN NullableCol1 IS NOT NULL THEN ROW_NUMBER() OVER (PARTITION BY GroupingColumn, CASE WHEN NullableCol1 IS NULL THEN 1 ELSE 0 END ORDER BY DateColumn DESC) AS NullableCol1RN,
CASE WHEN NullableCol2 IS NOT NULL THEN ROW_NUMBER() OVER (PARTITION BY GroupingColumn, CASE WHEN NullableCol2 IS NULL THEN 1 ELSE 0 END ORDER BY DateColumn DESC) AS NullableCol2RN
FROM dbo.YourTable)
SELECT GroupingColumn,
MAX(CASE NullableCol1RN WHEN 1 THEN NullableCol1 END) AS NullableCol1,
MAX(CASE NullableCol2RN WHEN 1 THEN NullableCol2 END) AS NullableCol2,
MAX(DateColumn) AS DateColumn
FROM CTE;
I am trying to show sum(grand_total) where is_loyal = 1 and sum(grand_total) where is_loyal = 0 together in the same result screen (grand_total column is in the same table for both). I have tried subqueries, join and case when but no luck so far. Is there a way to calculate and show the results together?
The only result I can find by subqueries is below, no matter how I change where clause gives me the same data. If I change my query a bit it throws error message
Msg 512, Level 16, State 1, Line 155
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Result
With conditional aggregation:
select date,
sum(case when loyal = 1 then grand_total end) [Total Loyalty],
sum(case when loyal = 0 then grand_total end) [Total Spent non-loyalty]
from tablename
group by date
use case when like this:
select
sum(case when loyal = 1 then 1 else 0 end) as grand_total_Loyal,
sum(case when loyal = 0 then 1 else 0 end) as grand_total_noneloyal
A simple method uses arithmetic:
select sum(loyal * grandtotal) as loyal_grandtotal,
sum( (1 - loyal) * grandtotal) as notloyal_grandtotal
from t
where loyal in (1, 0);
The more general solution is using a case expression. However, if you have 0/1 flags they fit very easily into arithmetic calculations -- on reason why they are preferred over, say, string flags.
I have a program that stores different types "Assets" for buildings which can be marked as being "Removed". I can create a query to count the number of assets by type and another to count the number of items identified as still present. But what I want to do is combine the two into 1 table.
Query 1
SELECT
theAssetOutletType, COUNT(id) AS TotalNoOfAssets
FROM
dbo.tblLEGAssets
WHERE
buildingID = 1
GROUP BY
theAssetOutletType
Query 2
SELECT
theAssetOutletType, COUNT(id) AS ItemsStillPresent
FROM
dbo.tblLEGAssets
WHERE
buildingID = 1 AND removed <> 0
GROUP BY
theAssetOutletType
Thank you in advance for any help
I would suggest conditional aggregation:
SELECT theAssetOutletType,
COUNT(*) as TotalNoOfAssets
SUM(CASE WHEN removed <> 0 THEN 1 ELSE 0 END) as ItemsStillPresent
FROM dbo.tblLEGAssets
WHERE buildingID = 1
GROUP BY theAssetOutletType;
This puts the values in separate columns on the same row -- which makes more sense to me than on separate rows.
Try Union:
SELECT theAssetOutletType, count(id) as TotalNoOfAssets FROM dbo.tblLEGAssets where buildingID=1 group by theAssetOutletType
UNION
SELECT theAssetOutletType, count(id) as ItemsStillPresent FROM dbo.tblLEGAssets where buildingID=1 and removed<> 0 group by theAssetOutletType
I've found a work around, by using a nested select - that I can use, but if it is still possible I'd love to know the answer:
SELECT theassetoutlettype,
noofitems,
noremoved,
noofitems - noremoved AS noLeft
FROM (SELECT theassetoutlettype,
Count(id) AS NoOfItems,
Sum(removed) AS noRemoved
FROM dbo.tbllegassets
WHERE buildingid = 1
GROUP BY theassetoutlettype) nested
I finally found a solution... took a lot of trial and error but it now works. Here is my Stored Procedure Solution, with the additional table "tblLEGAssetOutletTypes" which contains a single fields with a list of the 6 asset types. The following code will always return 6 rows with the project type, the total number of assets, the number of assets removed and the total remaining. Hope someone else who needs a similar problem resolving an use the code:
SELECT tblLEGAssetOutletTypes.assetOutletType, nested.NoOfItems, nested.noRemoved, NoOfItems-noRemoved AS noLeft
FROM (SELECT theAssetOutletType, COUNT(id) AS NoOfItems, SUM(removed) AS noRemoved
FROM tblLEGAssets
where buildingID=#buildingID
GROUP BY theAssetOutletType) AS nested
RIGHT JOIN tblLEGAssetOutletTypes ON nested.theAssetOutletType = tblLEGAssetOutletTypes.assetOutletType;
So I have 2 queries, 1 works like I would expect and the other one doesn't. Here's the one that works like I expect, it's a SUMIF using a CASE statement:
SELECT
PartNo,
SUM(ActualPcsGood) AS Pcs,
SUM(CASE WHEN Status = 'Current' THEN ActualPcsGood END) AS [Current],
SUM(CASE WHEN Status = 'Pending' THEN ActualPcsGood END) AS [Pending],
SUM(CASE WHEN Status = 'Future' THEN ActualPcsGood END) AS [Future],
SUM(CASE WHEN Status = 'Finished' THEN ActualPcsGood END) AS [Finished]
FROM OrderRouting
WHERE PartNo LIKE '20004%'
GROUP BY PartNo;
Output:
Now I have this other query that is confusing me, here's the code:
SELECT
JobNo,
UnitPrice,
SUM(CASE WHEN JobNo LIKE '10426%' THEN UnitPrice END) AS [OrderTotal]
FROM OrderDet
WHERE UnitPrice > 0
AND JobNo LIKE '10426%'
GROUP BY JobNo, UnitPrice;
Output:
My question is why is the 3rd column exactly the same as the second one? It's my intention that the third column is supposed to total the entire thing, meaning that the value for the 3rd column would be exactly the same for all rows. Why is it not? What is the major difference between my 2 examples?
This isn't tested but here are some ideas:
select dtl.JobNo, dtl.UnitPrice, tot.UnitPrice SumPrice, (dtl.UnitPrice/tot.UnitPrice)*100 pctTot
from
(SELECT
JobNo,
UnitPrice
FROM OrderDet
WHERE UnitPrice > 0
AND JobNo LIKE '10426%') dtl
cross join
(SELECT sum(UnitPrice) unitPrice
FROM OrderDet
WHERE UnitPrice > 0
AND JobNo LIKE '10426%') tot
OR
SELECT
JobNo,
UnitPrice,
(select sum(UnitPrice) from OrderDet where UnitPrice > 0 and JobNo like '10426%') totPrice
FROM OrderDet
WHERE UnitPrice > 0
AND JobNo LIKE '10426%'
GROUP BY JobNo;
At first look everything looks fine. I would say that your number of records per unit price is one per job for the this record set.
Add a COUNT(*) to see how many records are being summed up.
The other thought is that you have a quantity field on the record and your case statement should really be:
SUM(CASE WHEN JobNo LIKE '10426%' THEN UnitPrice * Quantity END) AS [OrderTotal]
Hope that helps.
Group by sums the values within the grouping columns. Your grouping columns are Job and UnitPrice. Because you have a unique JobNo, UnitPrice, it's hard to see what it's doing. Try adding a duplicate UnitPrice, JobNo row in your data source so you can see what's actually doing.
I'm not sure why you would want to show the sum total in this way though. I would use a rollup which would show the total at the bottom.
Your first query groups by only PartNo. So you SUM with Case statement work for each unique PartNo.
Your second query however groups by JobNo and UnitPrice. so your SUM runs for each group of JobNo and UnitPrice, which is only single row. Hence same result as UnitPrice. Assuming each jobid as unique unit price try query below. You don't need CASE inside SUM as WHERE clause will take care of it.
SELECT
JobNo,
MIN(UnitPrice) as UnitPrice,
SUM(UnitPrice) AS [OrderTotal]
FROM OrderDet
WHERE UnitPrice > 0
AND JobNo LIKE '10426%'
GROUP BY JobNo;
But why are you adding a case when you are already filtering by '10426%' condition?
your query will return only those records with '10426%' as Job No. Grouping by both job and Unitprice will give you single row for different unit prices of same job. Below query should be enough. If you are having different unit price for '10426%', we cannot get unit price in a single row.
SELECT
JobNo,
SUM(UnitPrice) AS [OrderTotal]
FROM OrderDet
WHERE UnitPrice > 0
AND JobNo LIKE '10426%'
GROUP BY JobNo;
Here is my current SQL code,
select
coalesce(cast(machinename as varchar(28)), 'Grand Total:') as 'machinename',
(IsNull(cast(CRATE_SMALL / 60 as varchar(24)),'0') + ':' + IsNull(cast(CRATE_SMALL % 60 as varchar(24)),'0') ) as '1001' ,
(IsNull(cast(CRATE_MEDIUM / 60 as varchar(24)),'0') + ':' + IsNull(cast(CRATE_MEDIUM % 60 as varchar(24)),'0'))as '1002',
(IsNull(cast(NO_SCHEDULE / 60 as varchar(24)),'0') + ':' + IsNull(cast(NO_SCHEDULE % 60 as varchar(24)),'0')) as '9999'
from (
select machinename ,
sum(case when vfrm.job_id = '1001' then DateDiff(mi, 0, total_time) end) as CRATE_SMALL ,
sum(case when vfrm.job_id = '1002' then DateDiff(mi, 0, total_time) end) as CRATE_MEDIUM ,
sum(case when vfrm.job_id = '9999' then DateDiff(mi, 0, total_time) end) as NO_SCHEDULE
from ven_fullreportmaster vfrm
INNER JOIN ven_descriptionmaster VDM ON VDM.description_id = vfrm..description_id
inner join ven_machinemaster vm on vm.machine_id = vfrm..machine_id
where vfrm.entry_date = convert(varchar, getdate()-7, 105)
and vfrm.shift_id =1
and vfrm.is_task_completed ='Y'
group by machinename with rollup
) as SubQueryALias
the output :
machinename 1001 1002 9999
ARISTECH 0:0 0:0 10:0
FADAL 0:0 5:0 10:0
Grand Total: 0:0 5:0 20:0
problem:
is there a anyway to show only the column(s) whose total is greater than zero...
So, in the above example I dont want to show the column name '1001'.
In all honesty, you shouldn't. This is a display issue, and as such should be dealt with when you display the data, not retrieve it from the database.
Reports, and datagrids and the like generally have functionality to do exactly this. Perhaps give more info about how you are displaying the data and someone might be able to provide more info.
To add the totals row, you ought to look at the ROLLUP part of the GROUP BY clause. This can produce sub-totals as well as the grand total, depending on what your final requirements are.
For hiding a column, there's not a solution for that in tsql - a SELECT statement always produces result sets with the same shape (names and types of columns).
But both may be better served by a reporting tool, if that's where this data is going (which I expect it is, given the nature of the query). Reporting tools tend to have better post-processing facilities.
try this:
select
entry_date, machinename, [1001], [1002], [9999], [1001]+[1002]+[9999] as Total
FROM ( --your query here
) d
WHERE [1001]+[1002]+[9999]>0
how about use a temp table to store the data query out and then build the output data from the temp table?
just
1.{your select query} into #t {your from where query}
2.select entry_date, machinename, [1001], [1002], [9999] from #t
union select '' as entry_date, 'total', sum([1001]), sum([1002]), sum([9999]) from #t
the logic is more clear with these steps, however, you can also use similar subquery to get the same result