March 21, 2013 at 3:49 pm
Hello,
Here is my scenario. Let's say I have two tables "Car" and "CarPart". The car consist of many parts and each part can belong to mutliple cars. One complication in my case is that each part gets a new PartID, even if it's the same part, which simply belongs to a different car. This is something I have no control over, so just bear with me. Here is the script to set things up.
IF OBJECT_ID('Car') IS NOT NULL DROP TABLE Car
CREATE TABLE Car (
CarID INT,
CarName VARCHAR(16)
)
IF OBJECT_ID('CarPart') IS NOT NULL DROP TABLE CarPart
CREATE TABLE CarPart (
PartID INT,
PartName VARCHAR(16),
CarID INT
)
INSERT INTO Car
VALUES (1, 'Chevy'),
(2, 'Ford'),
(3, 'Toyota'),
(4, 'Honda'),
(5, 'Nissan')
INSERT INTO CarPart
VALUES (110, 'Engine', 1),
(120, 'Engine', 2),
(210, 'Door', 1),
(220, 'Door', 3),
(310, 'Seat', 4),
(320, 'Seat', 5),
(410, 'Window', 3),
(510, 'Wheel', 2)
As you can see, the part "Engine" belongs to both "Chevy" and "Ford" and is listed twice with different IDs. Once again, this is a design limitation I have to live with. Here is what I need to accomplish. Given a car, I need to find all of the parts for this car and all of the other cars that these parts belong to. I have to continue finding parts and cars in a recursive manner until I reach the end of the chain. I tried this query:
DECLARE @StartCar VARCHAR(16) = 'Chevy'
;WITH cte (CarName, PartName)
AS
(
SELECT c.CarName, cp.PartName
FROM CarPart cp
JOIN Car c
ON cp.CarID = c.CarID
WHERE c.CarName = @StartCar
UNION ALL
SELECT c.CarName, cp.PartName
FROM CarPart cp
JOIN Car c
ON cp.CarID = c.CarID
JOIN cte cte
ONcp.PartName = cte.PartName
)
SELECT CarName, PartName
FROM cte
However, it gets into an infinite loop and terminates. I would expect see the output similar to this:
CarName PartName
Chevy Engine
Chevy Door
Ford Engine
Ford Wheel
Toyota Door
Toyota Window
I appreciante any pointers.
Thank you!
March 21, 2013 at 5:59 pm
Hi
Does this do what you require?
;with originalCar AS (
SELECT CarName, PartName
FROM Car c INNER JOIN CarPart cp ON c.CarID = cp.CarID
WHERE CarName = @StartCar
),
otherCars AS (
select carName, partName
FROM Car c INNER JOIN CarPart cp ON c.CarID = cp.CarID
WHERE carName in (
SELECT carName
FROM Car c INNER JOIN CarPart cp ON c.CarID = cp.CarID
WHERE PartName in (SELECT PartName FROM originalCar)
and CarName != @startCar
)
)
select carname, partname
from originalCar
union all
select carname, partname
from otherCars
order by CarName
March 22, 2013 at 11:16 am
First of all thank you for replying! It's close, but not quite. As I said, I need to get the entire chain Car -> CarPart -> Car -> CarPart, etc. The query you wrote gets the cars and their parts, which are directly related to the @StartCar and its parts. However, it doesn't get the rest of the chain. If I were to revise my setup code to this:
IF OBJECT_ID('Car') IS NOT NULL DROP TABLE Car
CREATE TABLE Car (
CarID INT,
CarName VARCHAR(16)
)
IF OBJECT_ID('CarPart') IS NOT NULL DROP TABLE CarPart
CREATE TABLE CarPart (
PartID INT,
PartName VARCHAR(16),
CarID INT
)
INSERT INTO Car
VALUES (1, 'Chevy'),
(2, 'Ford'),
(3, 'Toyota'),
(4, 'Honda'),
(5, 'Nissan'),
(6, 'Hugo')
INSERT INTO CarPart
VALUES (110, 'Engine', 1),
(120, 'Engine', 2),
(210, 'Door', 1),
(220, 'Door', 3),
(310, 'Seat', 4),
(320, 'Seat', 5),
(410, 'Window', 3),
(510, 'Wheel', 2),
(420, 'Window', 6)
As you can see, I added "Hugo", which will not get returned because it's not related to the @StartCar "Chevy" or its parts.
I still think it has to be some sort of recursive CTE, but I can't figure out how to stop it and prevent infinite loop.
March 22, 2013 at 11:29 am
I don't see a need for a recursive query:
IF OBJECT_ID('Car') IS NOT NULL DROP TABLE Car;
CREATE TABLE Car (
CarID INT,
CarName VARCHAR(16)
);
IF OBJECT_ID('CarPart') IS NOT NULL DROP TABLE CarPart;
CREATE TABLE CarPart (
PartID INT,
PartName VARCHAR(16),
CarID INT
);
INSERT INTO Car
VALUES (1, 'Chevy'),
(2, 'Ford'),
(3, 'Toyota'),
(4, 'Honda'),
(5, 'Nissan'),
(6, 'Hugo');
INSERT INTO CarPart
VALUES (110, 'Engine', 1),
(120, 'Engine', 2),
(210, 'Door', 1),
(220, 'Door', 3),
(310, 'Seat', 4),
(320, 'Seat', 5),
(410, 'Window', 3),
(510, 'Wheel', 2),
(420, 'Window', 6);
select
c.CarName,
cp.PartName
from
dbo.Car c
inner join dbo.CarPart cp
on (c.CarID = cp.CarID)
order by
c.CarName;
IF OBJECT_ID('Car') IS NOT NULL DROP TABLE Car;
IF OBJECT_ID('CarPart') IS NOT NULL DROP TABLE CarPart;
March 22, 2013 at 11:56 am
Lynn,
Thank you for your reply! The query you listed will return all of the cars and their parts, but this is not what I am after. My goal is as follows: given the initial "StartCar" find all of its parts, next find all of the cars which have the same parts (note in my original post that the parts get new numbers when they belong to different cars, so I have to detect the fact that the part is "shared" based on the PartName), repeat the process finding cars and their parts until I reach the end of the chain.
In my example above, I would expect "Honda" and "Nissan" NOT to be returned since they don't share any parts with cars, which share parts with my @StartCar of "Chevy".
Thank you!
March 22, 2013 at 1:08 pm
mishaluba (3/22/2013)
Lynn,Thank you for your reply! The query you listed will return all of the cars and their parts, but this is not what I am after. My goal is as follows: given the initial "StartCar" find all of its parts, next find all of the cars which have the same parts (note in my original post that the parts get new numbers when they belong to different cars, so I have to detect the fact that the part is "shared" based on the PartName), repeat the process finding cars and their parts until I reach the end of the chain.
In my example above, I would expect "Honda" and "Nissan" NOT to be returned since they don't share any parts with cars, which share parts with my @StartCar of "Chevy".
Thank you!
I'm not sure what you mean by share parts. The only thing they share in your sample data is the same name (Engine, for example). None of the parts listed have the same part number.
March 22, 2013 at 1:24 pm
Yes, that is correct. By "share parts" I mean that they share a part by the same name. That is why my "attempted" recursive query does a JOIN on PartName rather than PartID. Unfortunately this is a limitation of the design I have to live with. Also note that this is just an example to illustrate the problem (the real database is not about cars or parts). So once again, what I need to do is as follows:
@StartCar --> Parts of a @StartCar --> Other parts by the same name --> get Id's of those "other" parts --> Get cars which "own" those parts --> start over and repeat until the end of the chain is reached.
Does this help?
March 22, 2013 at 3:57 pm
I would expect, considering the number of parts in a car and the number of shared parts across cars that your recursive query is probably going to return all cars and all car parts when fully explored anyway.
March 22, 2013 at 4:53 pm
Thank you, but as I said, the Cars and CarParts was used by me simply to illustrate the problem. The actual database has nothing to do with either and the query will only return a fraction of avaialbe data.
March 22, 2013 at 11:07 pm
Here is the solution to this problem, which works correctly (credit to Steve Kass: http://stackoverflow.com/users/139164/steve-kass):
WITH cte(CarID,hier) AS (
SELECT CarID, CAST('/'+LTRIM(CarID)+'/' AS varchar(max))
FROM Car
WHERE CarName = @StartCar
UNION ALL
SELECT c2.CarID, hier+LTRIM(c2.CarID)+'/'
FROM Car AS c
JOIN cte ON cte.CarID = c.CarID
JOIN CarPart AS c1 ON c.CarID = c1.CarID
JOIN CarPart AS c2 ON c2.PartName = c1.PartName
WHERE hier NOT LIKE '%/'+LTRIM(c2.CarID)+'/%'
)
SELECT
c.CarName, cp.PartName
FROM Car AS c
JOIN CarPart AS cp ON cp.CarID = c.CarID
JOIN cte on cte.CarID = c.CarID
Viewing 10 posts - 1 through 9 (of 9 total)
You must be logged in to reply to this topic. Login to reply
This website stores cookies on your computer.
These cookies are used to improve your website experience and provide more personalized services to you, both on this website and through other media.
To find out more about the cookies we use, see our Privacy Policy