query with calculations most efficient way - sql

I have written the query below which works fine & produces the correct results. However I feel this is probably not terribly efficient as my SQL experience is quite limited.
The main thing that sticks out is where I calculate the nominal differences & price differences, these two lines.
1. isnull(hld.Nominal, 0) - isnull(nav.Nominal, 0) NomDiff
2. isnull((hld.Price / nav.LocalPrice - 1) * 100, 0)
Because I also have to put both these lines in the where condition, so the same calculations are being calculated twice. What is a better way of writing this query?
;WITH hld AS
(
SELECT id,
name,
Nominal,
Price
FROM tblIH
),
nav AS
(
SELECT id,
name,
Nominal,
LocalPrice
FROM tblNC
)
SELECT COALESCE(hld.id, nav.id) id,
COALESCE(nav.name, hld.name) name,
ISNULL(hld.Nominal, 0) HldNom,
ISNULL(nav.Nominal, 0) NavNom,
ISNULL(hld.Nominal, 0) - ISNULL(nav.Nominal, 0) NomDiff,
ISNULL(hld.Price, 0) HldPrice,
ISNULL(nav.LocalPrice, 0) NavPrice,
ISNULL((hld.Price / nav.LocalPrice - 1) * 100, 0)
FROM hld
FULL OUTER JOIN nav ON hld.id = nav.id
WHERE ISNULL(hld.Nominal, 0) - ISNULL(nav.Nominal, 0) <> 0
OR ISNULL((hld.Price / nav.LocalPrice - 1) * 100, 0) <> 0

First you select without where condition, you have result as table tmp, then you add where condition with column NomDiff and PriceDiff
;WITH hld AS
(
SELECT id,
name,
Nominal,
Price
FROM tblIH
),
nav AS
(
SELECT id,
name,
Nominal,
LocalPrice
FROM tblNC
)
select *
from (SELECT COALESCE(hld.id, nav.id) id,
COALESCE(nav.name, hld.name) name,
ISNULL(hld.Nominal, 0) HldNom,
ISNULL(nav.Nominal, 0) NavNom,
ISNULL(hld.Nominal, 0) - ISNULL(nav.Nominal, 0) NomDiff,
ISNULL(hld.Price, 0) HldPrice,
ISNULL(nav.LocalPrice, 0) NavPrice,
ISNULL((hld.Price / nav.LocalPrice - 1) * 100, 0) PriceDiff
FROM hld
FULL OUTER JOIN nav ON hld.id = nav.id) tmp
where NomDiff <> 0 or PriceDiff <> 0

The calculation part you can include in the second CTE, then you can simply select or filter the calculated field as a normal column in your final select query, without further calculations.

Related

Oracle SQL grouping based on value in blob field

This may not be possible
I have a table with a blob which contains XML and I want to get a count based on the content of the blob is there a way to do it in one query instead of doing
select count(*)
from MyTable
where dbms_lob.instr(bitstream, utl_raw.CAST_TO_RAW('ObjA'), 1, 1) > 0
select count(*)
from MyTable
where dbms_lob.instr(bitstream, utl_raw.CAST_TO_RAW('ObjB'), 1, 1) > 0
select count(*)
from MyTable
where dbms_lob.instr(bitstream, utl_raw.CAST_TO_RAW('ObjC'), 1, 1) > 0
select count(*)
from MyTable
where dbms_lob.instr(bitstream, utl_raw.CAST_TO_RAW('ObjD'), 1, 1) > 0
select count(*)
from MyTable
where dbms_lob.instr(bitstream, utl_raw.CAST_TO_RAW('ObjE'), 1, 1) > 0
Would something like this do?
Store values you're looking for into a CTE, and then join that CTE to your table.
WITH
temp (obj)
AS
(SELECT *
FROM TABLE (sys.odcivarchar2list ('ObjA',
'ObjB',
'ObjC',
'ObjD',
'ObjE')))
SELECT t.obj, COUNT (*)
FROM mytable e
JOIN temp t
ON DBMS_LOB.INSTR (bitstream,
UTL_RAW.cast_to_raw (t.obj),
1,
1) > 0
GROUP BY t.obj;

How to sum the value of another sum from same select statement

I am trying sum the value of another sum in the same select statement and then I want to check the sum value in case statement. When I do it, it is working instead it is just gets individual value.
I have to sum Billable_Trades and then I have to give some rate if the billable_trades is above some numbers for that, I need to know the total of the billable_trade.
select t.Business_Unit_Description, -- case when Product_Type_Description = 'Fee Based' then 'Fee Based' else '' end as revenue_type,
billable_trades,
isnull(c.comm_adjustments, 0) as commission_adjustments,
rate,
billable_trades*rate as charges,
0.3 as commission_rate,
isnull(c.comm_adjustments, 0)*0.3 as credit,
(billable_trades*rate)- isnull(c.comm_adjustments, 0)*0.3 as total
from
(
select Business_Unit_Description,
sum(billable_trades) as billable_trades,
CASE WHEN SUM(billable_trades) > 0 and SUM(billable_trades) <= 150000 THEN 0.85667 ELSE 0.47104 END as rate
from cte_combined
group by Business_Unit_Description
) t
left outer join cte_comm_adj c on c.Business_Unit_Description = t.Business_Unit_Description
order by t.Business_Unit_Description
There is obviously more to the query than is shown - as you are using a derived table to reference a CTE and also outer joining to another CTE.
I would move the calculation of rate out of the derived table:
Select t.Business_Unit_Description -- case when Product_Type_Description = 'Fee Based' then 'Fee Based' else '' end as revenue_type,
, t.sum_billable_trades
, commission_adjustments = isnull(c.comm_adjustments, 0)
, r.rate
, charges = t.sum_billable_trades * r.rate
, commission_rate = 0.3
, credit = isnull(c.comm_adjustments, 0) * 0.3
, total = (t.sum_billable_trades * r.rate) - isnull(c.comm_adjustments, 0) * 0.3
From (Select Business_Unit_Description
, sum_billable_trades = sum(billable_trades)
From cte_combined
Group By Business_Unit_Description) t
Cross Apply (Values (iif(t.sum_billable_trades > 0 And t.sum_billable_trades <= 150000, 0.85667, 0.47104))) As r(rate)
Left Outer Join cte_comm_adj c On c.Business_Unit_Description = t.Business_Unit_Description
Order By t.Business_Unit_Description;
I also wouldn't use the same name for the sum just to make it clearer.

Fetching Records from One table and making difference from same table gives irregular output

I want the sum from these two tables but I am getting seperately -
SELECT
GrandTotal - RecPayAmount -
(
select
sum(detail.LineAmount)
From
TranPOSDetail as detail
where
detail.RefHeaderCode = TranPOSHeader.Code
and
EntryFlag = 4
)
from TranPOSHeader
where
VoucherTypeCode=2000
And
WalkInCustomerCode=200429
And
GrandTotal > RecPayAmount
My Output is Like
1) 10
2) 20
But I want it like -
1) 30
How can I modify this query to reflect the results I want?
Use Cte and aggregate total values
WITH Amount
AS (
SELECT GrandTotal - RecPayAmount - (
SELECT sum(detail.LineAmount)
FROM TranPOSDetail AS detail
WHERE detail.RefHeaderCode = TranPOSHeader.Code
AND EntryFlag = 4
) TotalAmount
FROM TranPOSHeader
WHERE VoucherTypeCode = 2000
AND WalkInCustomerCode = 200429
AND GrandTotal > RecPayAmount
)
SELECT Sum(TotalAmount)
FROM Amount
Here is one simpler approach
SELECT Sum(GrandTotal - RecPayAmount - oa.Total_LineAmount)
FROM TranPOSHeader th
OUTER APPLY (SELECT Sum(d.LineAmount)
FROM TranPOSDetail AS d
WHERE d.RefHeaderCode = th.Code
AND d.EntryFlag = 4) oa (Total_LineAmount)
WHERE VoucherTypeCode = 2000
AND WalkInCustomerCode = 200429
AND GrandTotal > RecPayAmount
Without changing the existing query, you can try like following.
SELECT SUM(T.S) AS Total FROM
(
SELECT
(GrandTotal - RecPayAmount -
(
select
sum(detail.LineAmount)
From
TranPOSDetail as detail
where
detail.RefHeaderCode = TranPOSHeader.Code
and
EntryFlag = 4
)
) AS S
from TranPOSHeader
where
VoucherTypeCode=2000
And
WalkInCustomerCode=200429
And
GrandTotal > RecPayAmount
) T

Why is this query with a nested select faster when I include the where clause twice

I had a large sql query that had a nested select in the from clause.
Similar to this:
SELECT * FROM
( SELECT * FROM SOME_TABLE WHERE some_num = 20)
WHERE some_num = 20
In my sql query if I remove the outer "some_num" = 20 it takes 5 times as long . Shouldent these querys run in almost exactly the same time, if not wouldn't having the the additional where slow it down slightly?
What am I not understanding about how sql querys work?
Here is the original query in question
SELECT a.ITEMNO AS Item_No,
a.DESCRIPTION AS Item_Description,
UNITPRICE / 100 AS Retail_Price,
b.UNITSALES AS Units_Sold,
( Dollar_Sales ) AS Dollar_Sales,
( Dollar_Cost ) AS Dollar_Cost,
( Dollar_Sales ) - ( Dollar_Cost ) AS Gross_Profit,
( Percent_Page * c.PAGECOST ) AS Page_Cost,
( Dollar_Sales - Dollar_Cost - ( Percent_Page * c.PAGECOST ) ) AS Net_Profit,
Percent_Page * 100 AS Percent_Page,
( CASE
WHEN UNITPRICE = 0 THEN NULL
WHEN Percent_Page = 0 THEN NULL
WHEN ( Dollar_Sales - Dollar_Cost - ( Percent_Page * c.PAGECOST ) ) > 0 THEN 0
ELSE ( ceiling(abs(Dollar_Sales - Dollar_Cost - ( Percent_Page * c.PAGECOST )) / ( UNITPRICE / 100 )) )
END ) AS Break_Even,
b.PAGENO AS Page_Num
FROM (SELECT PAGENO,
OFFERITEM,
UNITSALES,
UNITPRICE,
( DOLLARSALES / 100 ) AS Dollar_Sales,
( DOLLARCOST / 10000 ) AS Dollar_Cost,
(( CAST(STUFF(PERCENTPAGE, 2, 0, '.') AS DECIMAL(9, 6)) )) AS Percent_Page
FROM OFFERITEMS
WHERE LEFT(OFFERITEM, 6) = 'CH1301'
AND PERCENTPAGE > 0) AS b
INNER JOIN ITEMMAST a
ON a.EDPNO = 1 * RIGHT(OFFERITEM, 8)
LEFT JOIN OFFERS c
ON c.OFFERNO = 'CH1301'
WHERE LEFT(OFFERITEM, 6) = 'CH1301'
ORDER BY Net_Profit DESC
Notice the two
WHERE left(OFFERITEM,6) = 'CH1301'
If I remove the outer Where then the query takes 5 times as long
As requested the Execution plan excuse the crappy upload
http://i.imgur.com/1PqmpVf.png
Is the column OFFERITEM in an index but PERCENTPAGE is not?
In your inner query you reference both these columns, in the outer query you only reference OFFERITEM.
Difficult to say without seeing the execution plan, but it could be that the outer query is causing the optimizer to run an 'index scan' whereas the inner query would cause a full table scan.
On a separate note, you should definitely modify:
WHERE left(OFFERITEM,6) ='CH1301'
to:
where offeritem like 'CH1301%'
As this will allow an index seek if there is an index on offeritem.

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!