I have two tables, one with routes and one with order delivery data.
I need to choose for each order exact route which has all shops in it, no more no less, order of shops is not important. If I don't have exact route, I don't want to join any route to that order.
RouteId ShopId
------------------------------------
1 111
1 222
2 111
2 222
2 333
3 111
3 222
3 333
3 444
OrderId ShopId
------------------------------------
11 111
11 222
11 333
12 111
12 222
13 111
13 333
I want result set like this
OrderId ShopId RouteId
------------------------------------
11 111 2
11 222 2
11 333 2
12 111 1
12 222 1
13 111 NULL
13 333 NULL
I have around 50k-100k orders so I don't want to use cursors or any other loop if not necessary.
Any help is greatly appreciated.
Zoran
You can use FOR XML PATH in order to get into a string value all shops per OrderId and per RouteId:
;WITH RoutesCTE AS (
SELECT RouteId,
(SELECT ShopId
FROM routes AS r2
WHERE r2.RouteId = r1.RouteId
ORDER BY ShopId
FOR XML PATH('')) AS RouteShops
FROM routes AS r1
GROUP BY RouteId
), OrdersCTE AS (
SELECT OrderId, ShopId,
(SELECT ShopId
FROM orders AS o2
WHERE o2.OrderId = o1.OrderId
ORDER BY ShopId
FOR XML PATH('')) AS OrderShops
FROM orders AS o1
)
SELECT OrderId, ShopId, c2.RouteId
FROM OrdersCTE AS c1
LEFT JOIN RoutesCTE AS c2 ON c1.OrderShops = c2.RouteShops
In the above CTEs RouteShops and OrderShops hold an XML value of all shops per RouteId and OrderId respectively.
So, for example, RoutesCTE returns the following results:
RouteId RouteShops
-----------------------------------------------------------------
1 <ShopId>111</ShopId><ShopId>222</ShopId>
2 <ShopId>111</ShopId><ShopId>222</ShopId><ShopId>333</ShopId>
3 <ShopId>111</ShopId><ShopId>222</ShopId><ShopId>333</ShopId><ShopId>444</ShopId>
Comparing these two fields we can find exact matches (if any).
Demo here
Note: The above query may return more than one route per OrderId if the routes table contains RouteIds having the same combination of ShopIds. In this case you have to fiddle with the RoutesCTE (including a ROW_NUMBER in it for example).
I like the xml answer from Giorgos better but I had started on this
select t1.*
from
(
select cteS.OrderID, cteR.RouteID, count(*) as count
from cteS
join cteR
on cteS.StoreID = cteR.StoreID
group by cteS.OrderID, cteR.RouteID
) t1
join
(
select cteS.OrderID, count(*) as count
from cteS
group by cteS.OrderID
) t2
on t1.OrderID = t2.OrderID
and t2.count = t2.count
or
select OrderID, RouteID
from store
join route
on store.storeID = route.storeID
except
select OrderID, RouteID
from store
full outer join route
on store.storeID = route.storeID
where store.storeID is null or route.storeID is null
Use of LEFT JOIN put to NULL fields of secondary table (RouteTable) is there's no corrispondence in JOIN clause.
Try this:
SELECT O.*, R.RouteId
FROM OrderTable O
LEFT OUTER JOIN RouteTable R
ON O.ShopId = R.ShopId
Related
I have a table mt table
It stores transactions.
Each transaction has a billing and its joined by external_id field
I want to return data from mt table where the billing table has only one joined record.
So in this case i would return (mttable, id:4 and 2. It's external_id 111 and 222 has only one record in billing table)
mt table
id external_id
--- -----------
1 444
2 222
3 333
4 111
billing table
id external_id
--- -----------
3 444
4 444
5 333
6 333
7 222
8 111
You could just use a basic GROUP BY query here:
SELECT mt.id, mt.external_id
FROM mt
INNER JOIN billing b ON b.external_id = mt.external_id
GROUP BY mt.id, mt.external_id
HAVING COUNT(*) = 1;
Demo
Hmmm . . .
select mt.*
from mt
where (select count(*)
from billing b
where mt.external_id = b.external_id
) = 1;
You can do something like the following:
SELECT * FROM mt WHERE external_id IN
(SELECT external_id FROM billing GROUP BY external_id HAVING COUNT(*) = 1)
There are few other options also, Like NOT EXISTS:
select t.*
from mt t join billing b on t.external_id = b.external_id
where not exists
(select 1 from billing bb
where mt.external_id = b.external_id and b.id <> bb.id);
Or You can also use the analytical function count as follows:
select * from
(select t.*, count(1) over (partition by b.external_id) as cnt
from mt t join billing b on t.external_id = b.external_id)
where cnt = 1
I have two tables, info and transactions.
info looks like this:
customer ID Postcode
1 ABC 123
2 DEF 456
and transactions looks like this:
customer ID day frequency
1 1/1/12 3
1 3/5/12 4
2 4/6/12 2
3 9/9/12 1
I want to know which day has the highest frequency for each postcode.
I know how to reference from two different tables but im not too sure how to reference multiple columns based on their values to other columns.
The output should be something like this:
customer ID postcode day frequency
1 ABC 123 3/5/12 4
2 DEF 456 4/6/12 2
3 GHI 789 9/9/12 1
and so on.
You can filter with a correlated subquery:
select
i.*,
t.day,
t.frequency
from info i
inner join transactions t on t.customerID = i.customerID
where t.frequency = (
select max(t.frequency)
from info i1
inner join transactions t1 on t1.customerID = i1.customerID
where i1.postcode = i.postcode
)
Or, if your RBDMS supports window functions, you can use rank():
select *
from (
select
i.*,
t.day,
t.frequency,
rank() over(partition by i.postcode order by t.frequency desc)
from info i
inner join transactions t on t.customerID = i.customerID
) t
where rn = 1
I have three tables:
**1.FT_ViewItemMovement **
StoreID ItemLookupCode QTY
-------------------------------
1001 11121111 222
1001 11121111 1
1201 11121111 333
1201 11121111 2
2.Item
ItemLookupCode ID
------------------------
11121111 111
3.ItemDynamic
ItemID StoreID SnapShotQuantity
-------------------------------------
111 1201 50
111 1001 25
111 5000 75
111 7777 100
Expecting Result
ItemID StoreID QTY SnapShotQuantity
-------------------------------------
111 1201 335 50
111 1001 223 25
111 5000 0 75
111 7777 0 100
I Tried this following Query, But I didn't get
SELECT
ViewItemMovement.ItemLookupCode,
ViewItemMovement.StoreID,
Sum(ViewItemMovement.Quantity) Quantity,
ItemDynamic.SnapShotQuantity SanpShotQuantity
FROM
FT_ViewItemMovement ViewItemMovement
left join Item with(NoLock) on Item.ItemLookupCode = ViewItemMovement.ItemLookupCode
left join ItemDynamic with(NoLock) on ItemDynamic.ItemID = Item.ID and ItemDynamic.StoreID = ViewItemMovement.StoreID
WHERE brand = 'PEPSI'
Group By
ViewItemMovement.ItemLookupCode,
ViewItemMovement.StoreID,
ViewItemMovement.ItemDescription,
ViewItemMovement.Brand,
Item.Cost,
ItemDynamic.SnapShotQuantity
first I want to summarise the quanity of FT_ViewItemMovement.Qty by StoreID and ItemLookupCode and then if it matches the same itemID with ItemDynamic it has to bring SnapShotQuanity
If there is no FT_ViewItemMovement.Qty, then show FT_ViewItemMovement.Qty = 0 as Expecting output
Simplify your query by looking at what you need, which is two SUM's and a GROUP BY containing the other selections, while using StoreID from FT_ViewItemMovement to allow the SUM(v.QTY) to work.
SELECT id.ItemID, v.StoreID, SUM(v.QTY), SUM(id.SnapShotQuantity)
FROM FT_ViewItemMovement v
INNER JOIN Item i ON v.ItemLookupCode = i.ItemLookupCode
INNER JOIN ItemDynamic id ON i.ID = id.ItemID
GROUP BY id.ItemID, v.StoreID
Your GROUP BY clause looks too complicated. I think this is what you want:
SELECT vim.ItemLookupCode, vim.StoreID,
COALESCE(Sum(vim.Quantity), 0) as Quantity,
id.SnapShotQuantity as SanpShotQuantity
FROM FT_ViewItemMovement vim LEFT JOIN
Item i
ON i.ItemLookupCode = vim.ItemLookupCode LEFT JOIN
ItemDynamic id
ON id.ItemID = i.ID and id.StoreID = vim.StoreID
WHERE vim.brand = 'PEPSI'
GROUP BY vim.ItemLookupCode, vim.StoreID,
id.SnapShotQuantity;
Your WHERE clause references brand. I have no idea where that comes from based on your question.
With your data sample, you must select from ItemDynamic then use join other table.
I hope it will work for you.
SELECT
ItemDynamic.ItemID,
ItemDynamic.StoreID,
SUM(ViewItemMovement.Quantity) AS Quantity,
MAX(ItemDynamic.SnapShotQuantity) AS SanpShotQuantity
FROM
ItemDynamic with(NoLock)
left join Item with(NoLock) on Item.ID = ItemDynamic.ItemID
left join FT_ViewItemMovement ViewItemMovement on ViewItemMovement.ItemLookupCode = Item.ItemLookupCode and ViewItemMovement.StoreID = ItemDynamic.StoreID
Group By
ItemDynamic.ItemID,
ItemDynamic.StoreID
I have a table which contains data in this format.
ProductID ShipId
11 1
11 2
11 3
22 1
22 2
33 1
33 2
Now I want only the distinct product ids where ship id 3 is not associated.
Output should be
22,33 only.
I have used this query but it throws error.
Select distinct productid from X_product_ship group by productid having shipid <> 3
Please help.
Use a subquery in the where clause to exclude the products that has a shipid of three.
select distinct P1.ProductID
from dbo.X_product_ship as P1
where P1.ProductID not in (
select P2.ProductID
from dbo.X_product_ship as P2
where P2.ShipId = 3
)
SQL Fiddle
Or you could get creative in the having clause using a case statement.
select P.ProductID
from dbo.X_product_ship as P
group by P.ProductID
having max(case when P.ShipId = 3 then 1 else 0 end) = 0
SQL Fiddle
i have 3 tables Product Category and ProductCategory.
Product table:
ProductID ProductName
1 P1
2 P2
3 P3
Category table:
CategoryID CategoryName
1 C1
2 C2
3 C3
ProductCategory:
ProductID CategoryID
1 1
1 2
1 3
2 3
3 1
3 2
I need a query which returns products which fall under more than 1 categories. Based on the table data above the result would be:
ProductID ProductName
1 P1
3 P3
So i wrote a query to fetch all the ProductID's which have more than one CategoryID's as below:
select ProductID,count(CategoryID)
from ProductCategory
group by Productid
having count(CategoryID)>1)
But when i try to display product details using the below query i get an error:
select *
from Product
where ProductID in (
select ProductID,count(CategoryID)
from ProductCategory
group by Productid
having count(CategoryID)>1))
Is my query wrong? How do i get the required product details which fall in more than one categories?
Remove the COUNT() in the subquery. The result of the subquery when used on IN clause must have only one returned column.
SELECT *
FROM Product
WHERE ProductID IN
(
SELECT ProductID
FROM ProductCategory
GROUP BY Productid
HAVING count(CategoryID) > 1
)
SQLFiddle Demo
or by using JOIN
SELECT a.*
FROM Product a
INNER JOIN
(
SELECT ProductID
FROM ProductCategory
GROUP BY Productid
HAVING count(CategoryID) > 1
) b ON a.ProductID = b.ProductID
SQLFiddle Demo
You can try use CROSS APPLY Operator in SQL Server
SELECT DISTINCT C.ProductID,C.ProductName,A.CategoryID,A.Total
FROM Product C
CROSS APPLY (
Select CA.CategoryID,Total=COUNT(*)
From ProductCategory CA
Where C.ProductID=CA.ProductID
Group By CA.CategoryID Having COUNT(*)>1
) AS A
ORDER BY A.Total DESC
Take a look: http://explainextended.com/2009/07/16/inner-join-vs-cross-apply/