Query to segregate multiple data which is inserted into single column - sql

There is a machine which is made with multiple parts, those parts were of two different country, the need is to find percentage of parts used from each country.
Machine
Parts_Used
% of IN part
% of CH parts
M_001
IN_001, CH_001, IN_002, CH002, IN_003, IN_004, IN_005,
M_002
IN_0011, CH_0011, IN_0012, CH0012, CH_0013, CH_0014, Ch_001
select count(*) as "% of CH parts"
from tablename
where Parts_Used like 'CH%';
Used this but did not get result.

Try this:
SELECT MP.[Machine]
,SUM(IIF(CHARINDEX('IN_', PU.[value]) > 0, 1, 0)) * 100.0 / COUNT(*) AS [% of IN part]
,SUM(IIF(CHARINDEX('CH_', PU.[value]) > 0, 1, 0)) * 100.0 / COUNT(*) AS [% of CH parts]
FROM machine_parts MP
CROSS APPLY STRING_SPLIT (Parts_Used, ',') PU
GROUP BY MP.[Machine]
The idea is to perform split of the values to now how may parts we have and easily perform the conditional counting with SUM and IIF. Basically, we have the following:
SELECT *
FROM machine_parts MP
CROSS APPLY STRING_SPLIT (Parts_Used, ',') PU
Then just counting.

Related

sql sum group by

I have following sql query
DECLARE #frameInt INT
SELECT TOP 1 #frameInt = gs.FrameGenerationInterval
FROM dbo.GlobalSettings gs
SELECT mti.InternalId,
b.InternalId AS BrandId,
CASE
WHEN DATEDIFF(second, mti.StartTime, mti.EndTime) / #frameInt > 0 THEN DATEDIFF(second, mti.StartTime, mti.EndTime) / #frameInt
ELSE 1
END AS ExposureAmount,
c.InternalId AS ChannelId,
c.Name AS ChannelName,
COALESCE( (p.Rating *
CASE
WHEN DATEDIFF(second, mti.StartTime, mti.EndTime) / #frameInt > 0 THEN DATEDIFF(second, mti.StartTime, mti.EndTime) / #frameInt
ELSE 1
END * CAST (17.5 AS decimal(8,2))
),CAST( 0 as decimal(8,2)) ) AS Equivalent
FROM dbo.MonitorTelevisionItems mti
LEFT JOIN dbo.Brands b ON mti.BrandId = b.InternalId
LEFT JOIN dbo.Channels c ON mti.ChannelId = c.InternalId
LEFT JOIN dbo.Programs p ON mti.ProgramId = p.InternalId
--WHERE mti.Date >= #dateFromLocal AND mti.Date <= #dateToLocal
GROUP BY mti.InternalId, mti.EndTime, mti.StartTime,
c.Name, p.Name, p.Rating,b.InternalId, c.InternalId
It gives following result
I would like it to return 1 row with sums of exposure amount and equivalent from all rows. Rest of the cells are the same apart from InternalId that I dont really need (i can remove it from query)
I am not very good at sql. Thank for help.
For the sake of posterity (and because it's cool to learn something new), here's the general solution to your problem (instead of a copy-and-paste ready solution for your specific case):
SELECT group_field1, group_field2, ...,
SUM(sum_field1), SUM(sum_field2), ...
FROM (...your original SQL...) AS someArbitraryAlias
GROUP BY group_field1, group_field2, ...
In your specific case, the group fields would be BrandId, ChannelId and ChannelName; the sum fields would be ExposureAmount and Equivalent.
Note: To ease readability (since your original SQL is quite complex), you can use a common table expression:
WITH someArbitraryAlias AS (
...your original SQL...
)
SELECT group_field1, group_field2, ...,
SUM(sum_field1), SUM(sum_field2), ...
FROM someArbitraryAlias
GROUP BY group_field1, group_field2, ...
Note that, when using common table expressions, the immediately preceding statement must be terminated with a semicolon.

how can I run this select statement inside a select statement without any problems?

I have a select statement that is pulling calculations, and one of the many things is a select in of itself. this is what I am trying to run:
SELECT Sum(AIRMODEL_NETWORK_SUMMARY.COMBINED_CASES) AS Total_Volume,
TRUNC(Avg(AIRMODEL_NETWORK_SUMMARY.COMBINED_CASES), 2) AS Average_Volume,
Sum(AIRMODEL_NETWORK_SUMMARY.CASES_PRODUCED) AS Producivity,
Sum(AIRMODEL_NETWORK_SUMMARY.STOCKING_LEVEL) AS Total_Inventory,
TRUNC(Avg(AIRMODEL_NETWORK_SUMMARY.STOCKING_LEVEL), 2) AS Average_Inventory,
Count(DISTINCT AIRMODEL_NETWORK_SUMMARY.LOC_ID) AS Nbr_of_plants,
TRUNC(Avg(AIRMODEL_NETWORK_SUMMARY.health_score), 2) AS Network_Capacity,
Sum(AIRMODEL_NETWORK_SUMMARY.Current_SQFT) AS total_SQFT,
Sum(AIRMODEL_NETWORK_SUMMARY.Current_AVAIL_SQFT) AS SumOfCurrent_AVAIL_SQFT,
Count(DISTINCT AIRMODEL_NETWORK_SUMMARY.Routes) AS nbr_of_Route,
TRUNC(Avg(AIRMODEL_NETWORK_SUMMARY.Routes), 2) AS Avg_nbr_Routes,
TRUNC(Avg(AIRMODEL_NETWORK_SUMMARY.DockDoor_Utilization), 2) AS AvgOfDockDoor_Utilization,
Sum(AIRMODEL_NETWORK_SUMMARY.Layer_Pickers) AS SumOfLayer_Pickers,
Sum(AIRMODEL_NETWORK_SUMMARY.nbr_canopies) AS SumOfnbr_canopies,
Sum(AIRMODEL_NETWORK_SUMMARY.nbr_DriveTHRUS) AS SumOfnbr_DrivETHRUS,
(Select Sum(MAX_TRUCKS_IN_CANOPY + MAX_TRUCKS_IN_DT_LOADING) from icam_locations),
Sum(AIRMODEL_NETWORK_SUMMARY.nbr_Returns) AS SumOfnbr_Returns,
TRUNC(Avg(AIRMODEL_NETWORK_SUMMARY.nbr_Returns), 2) AS AvgOfnbr_Returns,
trunc((Sum(OUT_OF_STOCK_QTY) / Sum(combined_cases)), 2) AS Out_of_Stock_Pct,
Sum(AIRMODEL_NETWORK_SUMMARY.CountOfINVEN_ID) AS SumOfCountOfINVEN_ID,
SUM(AIRMODEL_NETWORK_SUMMARY.SKU_CAPACITY) AS SUMOfSKU_CAPACITY,
Sum(AIRMODEL_NETWORK_SUMMARY.Sales) AS SumOfSales,
TRUNC(Avg(AIRMODEL_NETWORK_SUMMARY.Sales), 2) AS AvgOfSales
FROM AIRMODEL_NETWORK_SUMMARY
INNER JOIN ICAM_LOCATIONS
ON AIRMODEL_NETWORK_SUMMARY.LOC_ID = ICAM_LOCATIONS.LOCATION_ID
WHERE
TRIM((FSCL_YR_NUM * 100) +(FSCL_WK_IN_YR_NUM)) >= TRIM(('&PARM_FROM_DATE'))
AND TRIM((FSCL_YR_NUM * 100) +(FSCL_WK_IN_YR_NUM)) <= TRIM(('&PARM_TO_DATE'))
ORDER BY AIRMODEL_NETWORK_SUMMARY.LOC_ID
I was playing with it and also tried it as follows:
WITH STUFF_COUNT AS
(
SELECT ICAM_LOCATIONS.LOCATION_ID, (MAX_TRUCKS_IN_CANOPY+ MAX_TRUCKS_IN_DT_LOADING) AS VAL2
FROM ICAM_LOCATIONS
GROUP BY LOCATION_ID, MAX_TRUCKS_IN_CANOPY, MAX_TRUCKS_IN_DT_LOADING
)
SELECT SUM(Z.VAL2),
Sum(S.COMBINED_CASES) AS Total_Volume,
TRUNC(Avg(S.COMBINED_CASES), 2) AS Average_Volume,
Sum(S.CASES_PRODUCED) AS Producivity,
Sum(S.STOCKING_LEVEL) AS Total_Inventory,
TRUNC(Avg(S.STOCKING_LEVEL), 2) AS Average_Inventory,
Count(DISTINCT S.LOC_ID) AS Nbr_of_plants,
TRUNC(Avg(S.health_score), 2) AS Network_Capacity,
Sum(S.Current_SQFT) AS total_SQFT,
Sum(S.Current_AVAIL_SQFT) AS SumOfCurrent_AVAIL_SQFT,
Count(DISTINCT S.Routes) AS nbr_of_Route,
TRUNC(Avg(S.Routes), 2) AS Avg_nbr_Routes,
TRUNC(Avg(S.DockDoor_Utilization), 2) AS AvgOfDockDoor_Utilization,
Sum(S.Layer_Pickers) AS SumOfLayer_Pickers,
Sum(S.nbr_canopies) AS SumOfnbr_canopies,
SUM(S.NBR_DRIVETHRUS) AS SUMOFNBR_DRIVETHRUS,
Sum(S.nbr_Returns) AS SumOfnbr_Returns,
TRUNC(Avg(S.nbr_Returns), 2) AS AvgOfnbr_Returns,
trunc((Sum(OUT_OF_STOCK_QTY) / Sum(combined_cases)), 2) AS Out_of_Stock_Pct,
Sum(S.CountOfINVEN_ID) AS SumOfCountOfINVEN_ID,
SUM(S.SKU_CAPACITY) AS SUMOfSKU_CAPACITY,
Sum(S.Sales) AS SumOfSales,
TRUNC(AVG(S.SALES), 2) AS AVGOFSALES
FROM AIRMODEL_NETWORK_SUMMARY S, ICAM_LOCATIONS D, STUFF_COUNT Z
WHERE S.LOC_ID = D.LOCATION_ID
AND TRIM((FSCL_YR_NUM * 100) +(FSCL_WK_IN_YR_NUM)) >= TRIM(('&PARM_FROM_DATE'))
AND TRIM((FSCL_YR_NUM * 100) +(FSCL_WK_IN_YR_NUM)) <= TRIM(('&PARM_TO_DATE'))
ORDER BY S.LOC_ID;
neither of which has givin me desired results, but when I run them seperately, I get what is needed. Also, I am unable to create any additional tables to write too or the like. it appearently needs to be done as a whole (as demanded by the gods it is, or atleast by the paying company)
I typically get this error, and nome other random ones when I try adjustinng it.
SQL Error: ORA-00937: not a single-group group function.
Remove
ORDER BY S.LOC_ID
You cannot use there any cols which are not in the GROUP BY (or not an aggregate function / a select expr alias / a column number, for example ORDER BY 1)
P.S. and it doesn't make any sense in this query

Why colums in SELECT not belongs to SELECT

I have this select, but does not work.
select
a.code1,
a.data1,
a.stval,
(select sum(col1+col2+col3) from tad ) as sum1,
(select sum(col7+col8+col9) from tbac) as sum2,
CASE
WHEN (sum1+sum2) > 100 THEN (a.stval * sum1)
WHEN (sum1+sum2( <= 100 THEN (a.stval * sum2)
END as newdat1
from arti as a
Where is the error? why (sum1+sum2) its error?
Thanks
(sum1 + sum2) is an error because these identifiers are not defined in the scope where you are trying to use them. In an SQL select list, you cannot use symbols declared in the same select list, irrespective of their position on the list. Use a subquery if you need to access sum1 and sum2.
The specific reason is that SQL is a descriptive language that does not guarantee the order of evaluation of expressions. This is true in the select clause. This is true in the where clause. It is true in the from clause. SQL describes what the results look like. It does not prescribe the specific actions.
As a result, SQL does not allow identifiers defined in the select to be used in the same select clause (nor in the where clause at the same level). The expressions can be processed in any order.
The normal solution in your case is to use a subquery or a CTE. In your case, though, the subqueries are independent of the outer query (as written), so I would move them to the from clause:
select a.code1, a.data1, a.stval, x1.sum1, x2.sum2,
(CASE WHEN x1.sum1 + x2.sum2 > 100 THEN a.stval * x1.sum1
WHEN x1.sum1 + x2.sum2 <= 100 THEN a.stval * x2.sum2
END) as newdat1
from arti a cross join
(select sum(col1+col2+col3) as sum1 from tad ) x1 cross join
(select sum(col7+col8+col9) as sum2 from tbac) x2;
EDIT:
You can use a subquery or CTE. But there is an approach that builds on the above:
select a.code1, a.data1, a.stval, x1.sum1, x2.sum2,
(CASE WHEN x1.sum1 + x2.sum2 > 100 THEN a.stval * x1.sum1
WHEN x1.sum1 + x2.sum2 <= 100 THEN a.stval * x2.sum2
END) as newdat1
from arti a join
(select ascon, sum(col1+col2+col3) as sum1
from tad
group by ascon
) x1
on x1.ascon = arti.code1 cross join
(select sum(col7+col8+col9) as sum2 from tbac) x2;

Cumulative Percentage Categorization Query

I've got a query that's been driving me up the wall. The t-sql query is as follows but with about a thousand records in the source table, it's taking forever and a day to run. Is there any faster way of accomplishing the same task anyone might think of:
SELECT *, ROUND((SELECT SUM(PartTotal)
FROM PartSalesRankings
WHERE Item_Rank <= sub.Item_Rank) /
(SELECT SUM(PartTotal)
FROM PartSalesRankings) * 100, 2) as Cum_PC_Of_Total
FROM PartSalesRankings As sub
I'm trying to classify my inventory into A,B, and C categories based on percentage of cost, but it needs to be a cumulative percentage of cost, ie. 'A' parts make up 80% of my cost, 'B' parts make up the next 15%, and 'C' parts are the last 5%. There's obviously more to the sql statement than what I included, but the code I posted is the bottle-neck.
Any help would be greatly appreciated!
Aj
Try this:
;WITH SumPartTotal AS
(SELECT SUM(PartTotal) as SumPartTotal, Item_Rank
FROM PartSalesRankings
GROUP BY Item_Rank
),
CumSumPartTotal AS
(SELECT SUM(Sub.SumPartTotal) as CumSumPartTotal, SPT.Item_Rank
FROM SumPartTotal as SPT
INNER JOIN SumPartTotal as Sub ON
SPT.Item_Rank >= Sub.Item_Rank
GROUP BY SPT.Item_Rank
),
SumTotal AS
(SELECT SUM(PartTotal) as SumTotal
FROM PartSalesRankings
)
SELECT *,
ROUND((CumSumPartTotal.CumSumPartTotal * 100.0) / SumTotal.SumTotal, 2) as Cum_PC_Of_Total
FROM PartSalesRankings As sub
INNER JOIN CumSumPartTotal ON
sub.Item_Rank = CumSumPartTotal.Item_Rank
INNER JOIN SumTotal ON
sub.Item_Rank = SumTotal.Item_Rank
It should give your query a speed boost.
Sorry for the delay in response, but I've been up to my neck in other stuff, but after a great deal of trial and error I've found the following to be the fastest method:
Select companypartnumber
, (PartTotal + IsNull(Cum_Lower_Ranks, 0) ) / Sum(PartTotal) over() * 100 as Cum_PC_Of_Total
from PartSalesRankings PSRMain
Left join
(
Select PSRTop.Item_Rank, Sum(PSRBelow.PartTotal) as Cum_Lower_Ranks
from partSalesRankings PSRTop
Left join PartSalesRankings PSRBelow on PSRBelow.Item_Rank < PSRTop.Item_Rank
Group by PSRTop.Item_Rank
) as PSRLowerCums on PSRLowerCums.Item_Rank = PSRMain.Item_Rank
) sub2

Select rows from query with a distinct foreign key?

I am having trouble just now with yet another SQL problem. I really need to take some time out to learn this properly.
Anyway I have this query that someone else wrote and it gets values from a few different tables.
Now more than one item can have the same ProductID. So there may be 3 items returned all with the same ProductID but they have different descriptions etc.
I want to select only 1 item per ProductID. I have tried using DISTINCT and group by but I get a lot of errors. Also this is for an ACCESS database.
I think it's because of the logic used in the select query that is messing up my grouping.
Here is the query (I have tried formatting it a little better, used an online tool but its still a huge mess)
SELECT tblproducts.productid,
tblproducts.categorycode,
tblproducts.scaletitle,
tblproducts.picture,
tblitems.cost,
tblitems.modelnumber,
tblitems.itemid,
Iif([tblitems]![tradeapproved],Iif(([tblitems]![markup] / 100) <> 0,(Iif(([tblitems]![supplierdiscount] / 100) <> 0,
[tblitems]![cost] - ([tblitems]![cost] * ([tblitems]![supplierdiscount] / 100)),
[tblitems]![cost])) * ([tblitems]![markup] / 100),
0) + Iif(([tblitems]![supplierdiscount] / 100) <> 0,
[tblitems]![cost] - ([tblitems]![cost] * ([tblitems]![supplierdiscount] / 100)),
[tblitems]![cost]) + [tblitems]![tradeapprovedcost] + [tblitems]![shippingcost],
Iif(([tblitems]![markup] / 100) <> 0,(Iif(([tblitems]![supplierdiscount] / 100) <> 0,
[tblitems]![cost] - ([tblitems]![cost] * ([tblitems]![supplierdiscount] / 100)),
[tblitems]![cost])) * ([tblitems]![markup] / 100),
0) + Iif(([tblitems]![supplierdiscount] / 100) <> 0,
[tblitems]![cost] - ([tblitems]![cost] * ([tblitems]![supplierdiscount] / 100)),
[tblitems]![cost]) + [tblitems]![shippingcost]) AS price
FROM (tblitems
INNER JOIN tblproducts
ON tblitems.productid = tblproducts.productid)
INNER JOIN tblsuppliers
ON tblproducts.supplierid = tblsuppliers.supplierid
WHERE tblproducts.categorycode = 'BS'
AND tblitems.tradeapproved = 0
AND tblsuppliers.active = on
AND tblitems.isaccessory = false
ORDER BY Iif([tblitems]![tradeapproved],Iif(([tblitems]![markup] / 100) <> 0,(Iif(([tblitems]![supplierdiscount] / 100) <> 0,
[tblitems]![cost] - ([tblitems]![cost] * ([tblitems]![supplierdiscount] / 100)),
[tblitems]![cost])) * ([tblitems]![markup] / 100),
0) + Iif(([tblitems]![supplierdiscount] / 100) <> 0,
[tblitems]![cost] - ([tblitems]![cost] * ([tblitems]![supplierdiscount] / 100)),
[tblitems]![cost]) + [tblitems]![tradeapprovedcost] + [tblitems]![shippingcost],
Iif(([tblitems]![markup] / 100) <> 0,(Iif(([tblitems]![supplierdiscount] / 100) <> 0,
[tblitems]![cost] - ([tblitems]![cost] * ([tblitems]![supplierdiscount] / 100)),
[tblitems]![cost])) * ([tblitems]![markup] / 100),
0) + Iif(([tblitems]![supplierdiscount] / 100) <> 0,
[tblitems]![cost] - ([tblitems]![cost] * ([tblitems]![supplierdiscount] / 100)),
[tblitems]![cost]) + [tblitems]![shippingcost])
Can anyone post a quick fix for this? Thanks
Well, since you said you want to learn this stuff:
An inner join will connect Items to ProductId's but will result in a full set. So if you have 3 ProductIds and 1 Item you will get
ProdId ItemId Description
1 1 Handy Dandy Randy Sandy!
2 1 Easily Accessible personal grooming comb.
3 1 This item provides a man or woman with extra re...
So what you really want to do is get all the ItemIds:
select ItemId from Item_tbl
And then loop over each result, getting a single ProductId per Item:
select top 1 ProductId from Product_tbl where ItemId = 12345
Now anyone who suggests a loop with SQL gets yelled down, and (usually) rightly so. But this is a tough query to make, since it's not something people usually do.
You were along the right lines with group by. Group By says "consolidate all the rows that have distinct column X" where column X would be ItemId. So: Give me one row per ItemId.
Now you have to pick a ProductId from those 3 Products with ItemId 1. The cheater way to do it is not to pick a ProductId at random but rather a productId that fits a particular aggregate function. The most common are min and max.
select
ItemId,
max(ProductId)
from Itemtbl i
inner join Producttbl p
on i.itemid = p.itemId
group by ItemId
This will get the largest ProductId for each ItemId. You can do the same to get the minimum.
Now, what's trickier is finding a ProductId that fits a criteria - say the most recently updated. What you want to say is "select the ItemId, and the max(updatedDate), and then pull the ProductId of that max updatded date along - but that doesn't work in sql (dear god I wish it did though).
This query will give bad results:
select
ItemId,
max(ProductId),
max(updatdedDate)
from Itemtbl i
inner join Producttbl p
on i.itemid = p.itemId
group by ItemId
Because the max ProductId does not necessarily come from the row with the max updatedDate.
Instead you have to write a query that does this:
Selects the ItemId (e.g. 5), and the maxUpdated date (e.g. 5/5/2005)
Goes back to the Products_tbl and finds the ProductId whose ItemId is 5 and updatedDate is 5/5/2005
That query is left as an exercise. (but there's a bug! what if two products have the same last updated date and the same ItemId!)
First step to increase readability is to create a View for your tblItems that includes the fancy logic for Price, eg:
View [vwItemsWithAdjustedCost]
SELECT
ProductID,
TradeApproved,
IsAccessory,
Cost,
ModelNumber,
ItemID,
IIf(
( SupplierDiscount / 100 ) <> 0,
Cost - ( Cost * ( SupplierDiscount / 100 ) ),
Cost
) AS AdjustedCost
FROM tblItems
View [vwItemsWithPrice]
SELECT
ProductID,
TradeApproved,
IsAccessory,
Cost,
ModelNumber,
ItemID,
IIf(
( Markup / 100 ) <> 0,
AdjustedCost * ( Markup / 100 ),
0
)
+ AdjustedCost
IIf(
TradeApproved,
TradeApprovedCost,
0
)
+ ShippingCost AS Price
FROM vwItemsWithAdjustedCost
Next you have to decide what the criteria is for picking one item out of the many that match the same ProductID, if 3 items have the same ID which one do you want to show!?
As stated by Tom, an easy way is to just get the first (lowest) ID that matches, something like this:
SELECT
P.ProductID,
P.CategoryCode,
P.ScaleTitle,
P.Picture,
IP.Cost,
IP.ModelNumber,
IP.ItemID,
IP.Price
FROM
tblProducts P
INNER JOIN (
SELECT
ProductID,
MIN( ItemID ) AS MinItemID
FROM tblItems I
GROUP BY ProductID
) S
ON S.ProductID = P.ProductID
INNER JOIN vwItemsWithPrice IP
ON IP.ItemID = S.MinItemID
WHERE
P.CategoryCode = 'BS'
AND IP.TradeApproved = 0
AND IP.IsAccessory = false
ORDER BY IP.Price
This says for each ProductID, give me the first (lowest) ItemID from tblItems, and using that join to my view.
Hope this helps!