I need to pull back a set of data for a view where only rows with the the minimum cost are returned. I am joining 3 tables and they are big tables (225 million records a piece give or take) so performance is essential.
SELECT RIC.CarrierName, L.LoadGuid, RIC.RateIQCarrierid, RIRD.Cost
FROM tblLoads L
INNER JOIN RateIQRecord RIR ON L.LoadGuid = RIR.LoadGuId
INNER JOIN RateIQCarrier RIC ON RIR.RateIQRecordID = RIC.RateIQRecordID
INNER JOIN RateIQRateDetail RIRD ON RIC.RateIQRecordID = RIRD.RateIQRecordID
AND CAST(L.CreatedDate AS Datetime) Between '03/3/2014' and '03/3/2014 23:59:59.997'
Here is an example of the data set based of the code above
CarrierName LoadGuid Carrierid Cost
Carrier a FF98010A-90CE-4541-AB88-683645352712 210677951 192.51
Carrier a FF98010A-90CE-4541-AB88-683645352712 210677921 153.17
Carrier b FF98010A-90CE-4541-AB88-683645352712 210677925 196.28
Carrier b FF98010A-90CE-4541-AB88-683645352712 210677947 280.65
Carrier b FF98010A-90CE-4541-AB88-683645352712 210677949 241.71
Here is what I need:
CarrierName LoadGuid Carrierid Cost
Carrier a FF98010A-90CE-4541-AB88-683645352712 210677921 153.17
Carrier b FF98010A-90CE-4541-AB88-683645352712 210677925 196.28
Try this out:
Note:I'm assuming you have SQL Server 2008 or above. ROW_NUMBER() won;t work otherwise.
SELECT *
FROM
(
SELECT RIC.CarrierName,
L.LoadGuid,
RIC.RateIQCarrierid,
RIRD.Cost,
--Partition says look at each carrier as a group, then number them in order of cost lowest to highest.
ROW_NUMBER() OVER (PARTITION BY RIC.CarrierName ORDER BY Cost) rank_num
FROM tblLoads L
INNER JOIN RateIQRecord RIR
ON L.LoadGuid = RIR.LoadGuId
INNER JOIN RateIQCarrier RIC
ON RIR.RateIQRecordID = RIC.RateIQRecordID
INNER JOIN RateIQRateDetail RIRD
ON RIC.RateIQRecordID = RIRD.RateIQRecordID
--Don't do it this way
--AND CAST(L.CreatedDate AS Datetime) Between '03/3/2014' and '03/3/2014 23:59:59.997'
--Try this instead
AND CAST(L.CreatedDate AS DATE) = '03/03/2014'
) A
--Only grab the lowest number aka first of row number
WHERE A.rank_num = 1
Related
I'm trying to write an SQL query to return information including product code, yearly sales revenues, costs, sales people information from two different tables. I need to return the product ID information for the product with the lowest'onboarding cost' for the 'north' region.
I have used WHERE Region = 'North' to just get the product info for the North region, and ORDER BY onboarding cost; to sort this low to high and find the product with the lowest cost. Is there a way of just returning the product with the lowest onboarding cost for the north region?
In the WHERE clause you could add:
... AND onboarding cost=(SELECT MAX(onboarding cost) FROM ... WHERE region='North' ...)
SELECT
ProductRevenueAndCosts.ProductID,
ProductRevenueAndCosts.SalesRevenueYear1,
ProductRevenueAndCosts.SalesRevenueYear2,
ProductRevenueAndCosts.OperationalCostsYear1,
ProductRevenueAndCosts.OperationalCostsYear2,
ProductRevenueAndCosts.OnboardingCost,
SalesPeople.FirstName,
SalesPeople.LastName,
SalesPeople.Region
FROM SalesPeople INNER JOIN ProductRevenueAndCosts ON
SalesPeople.SalesPersonID = ProductRevenueAndCosts.SalesPersonID
WHERE SalesPeople.Region = "North"
AND ProductRevenueAndCosts.OnboardingCost=(
SELECT MAX(ProductRevenueAndCosts.OnboardingCost)
FROM SalesPeople INNER JOIN ProductRevenueAndCosts ON
SalesPeople.SalesPersonID = ProductRevenueAndCosts.SalesPersonID
WHERE SalesPeople.Region = "North"
)
ORDER BY ProductRevenueAndCosts.Onboardingcost DESC -- this is obsolete
;
But this will eventually return more than one result - in case that more than one product has the same highest onboaring cost.
Yes, you need to limit the number of records, like this:
MySQL, PostgreSQL
SELECT
ProductRevenueAndCosts.ProductID,
ProductRevenueAndCosts.SalesRevenueYear1,
ProductRevenueAndCosts.SalesRevenueYear2,
ProductRevenueAndCosts.OperationalCostsYear1,
ProductRevenueAndCosts.OperationalCostsYear2,
ProductRevenueAndCosts.OnboardingCost,
SalesPeople.FirstName,
SalesPeople.LastName,
SalesPeople.Region
FROM SalesPeople INNER JOIN ProductRevenueAndCosts ON
SalesPeople.SalesPersonID = ProductRevenueAndCosts.SalesPersonID
WHERE SalesPeople.Region = "North"
ORDER BY ProductRevenueAndCosts.Onboardingcost DESC
LIMIT 0, 1;
Where 0 is the starting index (first row) and 1 is the number of records you want to get.
SQL Server
SELECT TOP 1
ProductRevenueAndCosts.ProductID,
ProductRevenueAndCosts.SalesRevenueYear1,
ProductRevenueAndCosts.SalesRevenueYear2,
ProductRevenueAndCosts.OperationalCostsYear1,
ProductRevenueAndCosts.OperationalCostsYear2,
ProductRevenueAndCosts.OnboardingCost,
SalesPeople.FirstName,
SalesPeople.LastName,
SalesPeople.Region
FROM SalesPeople INNER JOIN ProductRevenueAndCosts ON
SalesPeople.SalesPersonID = ProductRevenueAndCosts.SalesPersonID
WHERE SalesPeople.Region = "North"
ORDER BY ProductRevenueAndCosts.Onboardingcost DESC;
Where TOP 1 tells the RDBMS that you are interested only in the very first row.
Oracle
SELECT
ProductRevenueAndCosts.ProductID,
ProductRevenueAndCosts.SalesRevenueYear1,
ProductRevenueAndCosts.SalesRevenueYear2,
ProductRevenueAndCosts.OperationalCostsYear1,
ProductRevenueAndCosts.OperationalCostsYear2,
ProductRevenueAndCosts.OnboardingCost,
SalesPeople.FirstName,
SalesPeople.LastName,
SalesPeople.Region
FROM SalesPeople INNER JOIN ProductRevenueAndCosts ON
SalesPeople.SalesPersonID = ProductRevenueAndCosts.SalesPersonID
WHERE SalesPeople.Region = "North"
ORDER BY ProductRevenueAndCosts.Onboardingcost DESC
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY;
The three tables that I'm linking are item_scan_fact, member_dimension and store_dimension. So far this is what I have:
SELECT
store_dimension.store_number,
member_dimension.member_number
COUNT (item_scan_fact.visit_number) AS NumVisits
FROM
member_dimension,
item_scan_fact
INNER JOIN store_dimension
ON item_scan_fact.member_key = member_dimension.member_key
AND item_scan_fact.store_key = store_dimension.store_key
GROUP BY
store_dimension.store_number,
member_dimension.member_number, NumVisits;
On the surface it appears solvable with a couple Common Table Expressions
Does this help point you in the right direction?
WITH s1 -- JJAUSSI: Find the visit_number_count by member_key and store_key
AS
(SELECT isf.member_key
,isf.store_key
-- JJAUSSI: DISTINCT resolves a potential 1:N (one to many) relationship here
,COUNT( DISTINCT isf.visit_number) AS visit_number_count
FROM item_scan_fact isf
GROUP BY isf.member_key
,isf.store_key),
s2 -- JJAUSSI: Find the visit_number_count_max by member_key
AS
(SELECT s1.member_key
,MAX(s1.visit_number_count) AS visit_number_count_max
FROM s1
GROUP BY s1.member_key)
-- JJAUSSI: Use this version to see the list of store_key values
-- that have the visit_number_count_max value. This has the potential
-- to be a 1:N relationship.
SELECT s1.member_key
,md.member_number
,s1.store_key
,sd.store_number
,s1.visit_number_count
FROM s2 INNER JOIN s1
ON s2.member_key = s1.member_key
AND s2.visit_number_count_max = s1.visit_number_count
INNER JOIN store_dimension sd
ON sd.store_key = s1.store_key
INNER JOIN member_dimension md
ON md.member_key = s1.member_key;
If this is what you were going for...congratulations! On to the next query!
If you ultimately are after a single store_key response for each member_key (basically you want to determine the member_key's "primary" store_key) then an additional step is probably needed (depending on your data).
Here are some ideas:
Evaluate the member_key based on some other summable facet of
item_scan_fact (like total price paid?)
If you consider all store_key values of equal merit that have the same visit_number_count_max value for a given member_key, just choose a store_key with MAX or MIN
You would seem to want:
SELECT member_number, MAX(NumVisits)
FROM (SELECT sd.store_number, md.member_number
COUNT(*) AS NumVisits
FROM member_dimension md JOIN
item_scan_fact isf
ON md.member_key = isf.member_key JOIN
store_dimension sd
ON isf.store_key = sd. store_key
GROUP BY sd.store_number, md.member_number
) sm
GROUP BY member_number;
If you want to return both the max and the matching customer number you can apply a Teradata SQL extension, qualify:
SELECT sd.store_number, md.member_number
COUNT(*) AS NumVisits
FROM member_dimension md JOIN
item_scan_fact isf
ON md.member_key = isf.member_key JOIN
store_dimension sd
ON isf.store_key = sd. store_key
GROUP BY sd.store_number, md.member_number
QUALIFY
rank() -- might return multiple rows with the same max, ROW_NUMBER a single row
over (partition by sd.store_number
order by NumVisits desc) = 1
So I have to use a query where I list the trade id stock id and the total price converted to us dollars where it is the highest price total.
SELECT
tr.trade_id, tr.stock_id, round(tr.price_total * con.exchange_rate,2)
as "US Dollars"
from trade tr
JOIN stock_exchange se
on se.STOCK_EX_ID = tr.STOCK_EX_ID
JOIN currency curr
on curr.CURRENCY_ID = se.currency_id
JOIN conversion con
on con.from_CURRENCY_ID = curr.CURRENCY_ID
WHERE (tr.PRICE_TOTAL) = (Select Max(price_total) from trade) and curr.name =
'Dollar' and tr.stock_ex_id is not NULL
group by tr.trade_id, tr.stock_id, round(tr.price_total), tr.price_total,
round(tr.price_total * con.exchange_rate,2);
Trade (trade_id PK, stock_id FK2, transaction_time, shares, stock_ex_id FK1,price_total)
Stock-exchange( stock_ex_id PK, name, symbol, currency_id FK1)
conversion( from_currency_id PK, to_currency_id)
currency ( currency_id PK, name, symbol
expected output should be -
trade_id - 1 stock_id 1, price (non conversion) (225000000)
I'm not sure why in my output i get nothing. Any suggestions to fix this? Sorry if i did not format the question right
But you could start by seeing what your select max query returns, and then filtering down the trade table by that value. Trace this though all of your tables to see where the data on your trade table isn't on the others.
OR put you curr.name criteria up with the join and specify a LEFT JOIN for the last two tables to see if the data is missing like below:
SELECT tr.trade_id
,tr.stock_id
,round(tr.price_total * con.exchange_rate, 2) AS "US Dollars"
FROM trade tr
LEFT JOIN stock_exchange se
ON se.STOCK_EX_ID = tr.STOCK_EX_ID
LEFT JOIN currency curr
ON curr.CURRENCY_ID = se.currency_id
AND curr.name = 'Dollar'
LEFT JOIN conversion con
ON con.from_CURRENCY_ID = curr.CURRENCY_ID
WHERE (tr.PRICE_TOTAL) = (SELECT Max(trm.price_total) FROM trade trm)
AND tr.stock_ex_id IS NOT NULL
It could be other things as well, but one spot to look is this
You say WHERE tr.PRICE_TOTAL = (Select Max(price_total) from trade)
AND curr.name = 'Dollar'
If the trade with the Maximum price_total is not in Dollars, then you'll get nothing.
You need to either change your "Select Max(price_total) from trade" to get maximum values for Dollar trades only, or else get all the valid trades in a subquery and then get the maximum value from that.
After comment below - to debug - run "Select * from trade where price_total = (Select Max(price_total) from trade)" to get the valid trade record(s). Then look at them - they are going to fail somewhere - no STOCK_EX_ID on trade, or one of the other joins fails - you need to start from known data and go from there.
I am having issues with my SQL code.
I want to display the total count of stores that correspond with a certain Distribution Center/ WH. I want both to be tied to a certain item.
For example: I have one WH that gave a certain item to 50 stores. I want the query to tell me if I give it the item# and WH# it will give me the amount of stores that received that item.
SELECT
COUNT(*) AS TOTAL_STORES
FROM
(SELECT
a.WH_i, a.STORE_i
FROM
WH a, STORES b
WHERE
a.item_i = 2201
AND a.WH_i IN (10)
GROUP BY
a.WH_i, B.STORE_i
HAVING
COUNT(a.item_i) = 1) a;
Table WH has the warehouse numbers and item numbers and store has the store numbers.
I am new to SQL so I am not 100% confident with joins just yet. Any help is greatly appreciated though!
EDIT: I tried joining the two tables without actually using the JOIN clause and it is still not giving proper results.
SELECT COUNT(*) AS TOTAL_STORES
FROM(
SELECT a.WH_i, b.STORE_i
FROM WH a, STORES b
WHERE a.item_i = b.sku_i
AND a.stock_i = b.stock_i
AND a.item_i = 2201
AND a.WH_i IN (10)
GROUP BY a.WH_i, B.STORE_i
HAVING COUNT(a.item_i) = 1
)a;
You only want the count of stores so just pull stores in your subquery.
SELECT COUNT(STORE_i) AS TOTAL_STORES
FROM(
SELECT b.STORE_i
FROM WH a
JOIN STORES b
ON a.item_i = b.sku_i
AND a.stock_i = b.stock_i
WHERE
a.item_i = 2201
AND a.WH_i = 10
GROUP BY b.STORE_i
HAVING COUNT(a.item_i) = 1
) a;
SELECT a.WH_i, a.item_i, count(DISTINCT b.store_i)
FROM WH a
JOIN STORES b
ON a.item_i = b.sku_i
GROUP BY a.WH_i, a.item_i
I am trying to convert currency rates on orders.
The currency rates table is updated daily End of day for that particular day, however an order can be created earlier in the day, and as a result the rate will not be reflected with the current query. How would I add case statements and modify the query where by if the currency rate for the day does not exist use the last currency date available (within the table).
#Curr is the desired currency code. Could be 'USD', 'GBP' etc.
SELECT OrderNumber
,(CASE WHEN #Curr<>cur.code THEN
o.price * (SELECT Rate FROM xchangeRates xr
WHERE xr.FromCurrCode = c.Code
AND xr.ToCurrCode = #Curr
AND xr.Date= ISNULL((SELECT TOP 1 CAST(Crtd_DateTime AS Date) FROM ApDoc apdoc
WHERE apdoc.PONbr = o.OrderNumber AND apdoc.PONbr>''
ORDER BY apdoc.Crtd_DateTime DESC),o.orderdate)
)
ELSE o.price
END ) as o.price
from orders o
join currency c on c.curcode = o.curcode
Why not this:
...
o.price * (SELECT TOP 1 Rate FROM xchangeRates xr
WHERE xr.FromCurrCode = c.Code
AND xr.ToCurrCode = #Curr
ORDER BY xr.Date DESC)
...
If there are future dates in the xchangeRates table, all you have to do is add an additional filter to the WHERE clause to limit xr.Date to <= today.
EDIT: to handle this requirement:
if the invoice was created in the apdoc the it uses that Date for the
exchange rate, but if not then it uses the date the order was created.
forget your subselect to apDoc and JOIN it to orders in the outer query instead:
LEFT OUTER JOIN apDoc
ON apdoc.PONbr = o.OrderNumber
And then do this for your subquery instead of what I have above:
o.price * (SELECT TOP 1 Rate FROM xchangeRates xr
WHERE xr.FromCurrCode = c.Code
AND xr.ToCurrCode = #Curr
AND xr.Date =< COALESCE(CAST(apdoc.Crtd_DateTime AS Date),o.OrderDate)
ORDER BY xr.Date DESC)
nb: I CAST Crtd_DateTime AS Date because you did it in your code, and for all I know it's a varchar, but if it's a Datetime datatype, then the cast isn't necessary in my solution.