sql server programming:
First, let me thank you for including the DDL and sample data. If only more folks would do that... The significant issue is that you are not using relational data. You are using name-value pairs where the Attrib can mean many different things. A better design would have separate columns for the significant characteristics, i.e., color, transmission, style, etc. The way you are approaching the problem works against having a 'simple' solution. As you noted, you will have to subquery or JOIN for each separate attrib. Not a nice prospect... IMO, The 'best' option IF you want to be able to handle the data as relational data is to redesign the table(s) to be relational. I know you didn't want to hear that, but ... -- Arnie Rowland, Ph.D. Westwood Consulting, Inc Most good judgment comes from experience. Most experience comes from bad judgment. - Anonymous You can't help someone get up a hill without getting a little closer to the top yourself. - H. Norman Schwarzkopf [quoted text, click to view] "Dave" <davefrick@newsgroup.nospam> wrote in message news:e7l3XEQEHHA.4992@TK2MSFTNGP03.phx.gbl... >I have a table that associates cars (vehicle ids) with attributes (color, >body style, transmission) and I want to return a list of all cars that have >certain attributes (e.g., a black car with manual transmission). > > The table looks like this... > > if object_id('tempdb..#attrib') IS NOT NULL > DROP TABLE #attrib > GO > CREATE TABLE #attrib > (veh_id int > ,attrib varchar(12) > ) > GO > INSERT #attrib (veh_id, attrib) VALUES(1, 'black') > INSERT #attrib (veh_id, attrib) VALUES(1, '2 door') > INSERT #attrib (veh_id, attrib) VALUES(1, 'manual') > INSERT #attrib (veh_id, attrib) VALUES(2, 'red') > INSERT #attrib (veh_id, attrib) VALUES(2, '4 door') > INSERT #attrib (veh_id, attrib) VALUES(2, 'manual') > > --check > SELECT * FROM #attrib > > > --I want all the black cars with manual transmission.... > > --this returns no matches > SELECT * > FROM #attrib > WHERE attrib='manual' and attrib='black' > > --this returns both cars > SELECT distinct veh_id > FROM #attrib > WHERE attrib IN ('manual', 'black') > > --this returns both cars > SELECT distinct veh_id > FROM #attrib > WHERE attrib ='manual' > UNION > SELECT distinct veh_id > FROM #attrib > WHERE attrib ='black' > > > --this returns only the black car with a manual transmission > SELECT distinct a1.veh_id > FROM #attrib a1 JOIN > (SELECT distinct veh_id > FROM #attrib > WHERE attrib ='black') As a2 > ON a1.veh_id=a2.veh_id > WHERE attrib ='manual' > > > I could also write it as a subquery but is there a better approach? > > What if I want to find all black cars with manual trsnamissions that are > 2 -doors? Then I have to add another join or subquery. I won't know > ahead of time how many attributes the search will be on. > > Thanks for any suggestions. > > > > >
You're probably going to get a lot of responses as to why your design is a bad one, so please be prepared. Why is your database using EAV (entity attribute value) design instead of normalization? What are you trying to accomplish? Stu [quoted text, click to view] Dave wrote: > I have a table that associates cars (vehicle ids) with attributes (color, > body style, transmission) and I want to return a list of all cars that have > certain attributes (e.g., a black car with manual transmission). > > The table looks like this... > > if object_id('tempdb..#attrib') IS NOT NULL > DROP TABLE #attrib > GO > CREATE TABLE #attrib > (veh_id int > ,attrib varchar(12) > ) > GO > INSERT #attrib (veh_id, attrib) VALUES(1, 'black') > INSERT #attrib (veh_id, attrib) VALUES(1, '2 door') > INSERT #attrib (veh_id, attrib) VALUES(1, 'manual') > INSERT #attrib (veh_id, attrib) VALUES(2, 'red') > INSERT #attrib (veh_id, attrib) VALUES(2, '4 door') > INSERT #attrib (veh_id, attrib) VALUES(2, 'manual') > > --check > SELECT * FROM #attrib > > > --I want all the black cars with manual transmission.... > > --this returns no matches > SELECT * > FROM #attrib > WHERE attrib='manual' and attrib='black' > > --this returns both cars > SELECT distinct veh_id > FROM #attrib > WHERE attrib IN ('manual', 'black') > > --this returns both cars > SELECT distinct veh_id > FROM #attrib > WHERE attrib ='manual' > UNION > SELECT distinct veh_id > FROM #attrib > WHERE attrib ='black' > > > --this returns only the black car with a manual transmission > SELECT distinct a1.veh_id > FROM #attrib a1 JOIN > (SELECT distinct veh_id > FROM #attrib > WHERE attrib ='black') As a2 > ON a1.veh_id=a2.veh_id > WHERE attrib ='manual' > > > I could also write it as a subquery but is there a better approach? > > What if I want to find all black cars with manual trsnamissions that are > 2 -doors? Then I have to add another join or subquery. I won't know ahead > of time how many attributes the search will be on. > > Thanks for any suggestions.
Create a table to hold your search conditions like so: CREATE TABLE #search(attrib VARCHAR(12) NOT NULL) INSERT #search VALUES('black') INSERT #search VALUES('manual') Then you can compare the matches in your attributes table against the number of rows in your search condition table: SELECT DISTINCT veh_id FROM #attrib WHERE ( SELECT COUNT(*) FROM #attrib a INNER JOIN #search s ON s.attrib = a.attrib AND a.veh_id = #attrib.veh_id ) = ( SELECT COUNT(*) FROM #search) Chris [quoted text, click to view] Dave wrote: > I have a table that associates cars (vehicle ids) with attributes (color, > body style, transmission) and I want to return a list of all cars that have > certain attributes (e.g., a black car with manual transmission). > > The table looks like this... > > if object_id('tempdb..#attrib') IS NOT NULL > DROP TABLE #attrib > GO > CREATE TABLE #attrib > (veh_id int > ,attrib varchar(12) > ) > GO > INSERT #attrib (veh_id, attrib) VALUES(1, 'black') > INSERT #attrib (veh_id, attrib) VALUES(1, '2 door') > INSERT #attrib (veh_id, attrib) VALUES(1, 'manual') > INSERT #attrib (veh_id, attrib) VALUES(2, 'red') > INSERT #attrib (veh_id, attrib) VALUES(2, '4 door') > INSERT #attrib (veh_id, attrib) VALUES(2, 'manual') > > --check > SELECT * FROM #attrib > > > --I want all the black cars with manual transmission.... > > --this returns no matches > SELECT * > FROM #attrib > WHERE attrib='manual' and attrib='black' > > --this returns both cars > SELECT distinct veh_id > FROM #attrib > WHERE attrib IN ('manual', 'black') > > --this returns both cars > SELECT distinct veh_id > FROM #attrib > WHERE attrib ='manual' > UNION > SELECT distinct veh_id > FROM #attrib > WHERE attrib ='black' > > > --this returns only the black car with a manual transmission > SELECT distinct a1.veh_id > FROM #attrib a1 JOIN > (SELECT distinct veh_id > FROM #attrib > WHERE attrib ='black') As a2 > ON a1.veh_id=a2.veh_id > WHERE attrib ='manual' > > > I could also write it as a subquery but is there a better approach? > > What if I want to find all black cars with manual trsnamissions that are > 2 -doors? Then I have to add another join or subquery. I won't know ahead > of time how many attributes the search will be on. > > Thanks for any suggestions.
[quoted text, click to view] Stu wrote: > You're probably going to get a lot of responses as to why your design > is a bad one, so please be prepared. Why is your database using EAV > (entity attribute value) design instead of normalization? What are you > trying to accomplish?
I have used this approach once, where we had a system that allowed different clients to capture different information about their customers, and this information wasn't of benefit to the core system. In other words, the client could defined their own data without it being a proper part of the database (e.g. customer's favorite pet, eye color, etc). But I agree, in the example shown, where the information being captured seems to be pretty standard then EAV isn't a good approach.
I have a table that associates cars (vehicle ids) with attributes (color, body style, transmission) and I want to return a list of all cars that have certain attributes (e.g., a black car with manual transmission). The table looks like this... if object_id('tempdb..#attrib') IS NOT NULL DROP TABLE #attrib GO CREATE TABLE #attrib (veh_id int ,attrib varchar(12) ) GO INSERT #attrib (veh_id, attrib) VALUES(1, 'black') INSERT #attrib (veh_id, attrib) VALUES(1, '2 door') INSERT #attrib (veh_id, attrib) VALUES(1, 'manual') INSERT #attrib (veh_id, attrib) VALUES(2, 'red') INSERT #attrib (veh_id, attrib) VALUES(2, '4 door') INSERT #attrib (veh_id, attrib) VALUES(2, 'manual') --check SELECT * FROM #attrib --I want all the black cars with manual transmission.... --this returns no matches SELECT * FROM #attrib WHERE attrib='manual' and attrib='black' --this returns both cars SELECT distinct veh_id FROM #attrib WHERE attrib IN ('manual', 'black') --this returns both cars SELECT distinct veh_id FROM #attrib WHERE attrib ='manual' UNION SELECT distinct veh_id FROM #attrib WHERE attrib ='black' --this returns only the black car with a manual transmission SELECT distinct a1.veh_id FROM #attrib a1 JOIN (SELECT distinct veh_id FROM #attrib WHERE attrib ='black') As a2 ON a1.veh_id=a2.veh_id WHERE attrib ='manual' I could also write it as a subquery but is there a better approach? What if I want to find all black cars with manual trsnamissions that are 2 -doors? Then I have to add another join or subquery. I won't know ahead of time how many attributes the search will be on. Thanks for any suggestions.
Thanks guys. Your point is well taken but if I could paraphrase one of America's favorite Secretaries of Defense: "You go to war with the schema you have, not the schema you wish you had." [quoted text, click to view] "Arnie Rowland" <arnie@1568.com> wrote in message news:e35LZOQEHHA.4508@TK2MSFTNGP02.phx.gbl... > First, let me thank you for including the DDL and sample data. If only > more folks would do that... > > The significant issue is that you are not using relational data. You are > using name-value pairs where the Attrib can mean many different things. A > better design would have separate columns for the significant > characteristics, i.e., color, transmission, style, etc. The way you are > approaching the problem works against having a 'simple' solution. As you > noted, you will have to subquery or JOIN for each separate attrib. Not a > nice prospect... > > IMO, The 'best' option IF you want to be able to handle the data as > relational data is to redesign the table(s) to be relational. > > I know you didn't want to hear that, but ... > > -- > Arnie Rowland, Ph.D. > Westwood Consulting, Inc > > Most good judgment comes from experience. > Most experience comes from bad judgment. > - Anonymous > > You can't help someone get up a hill without getting a little closer to > the top yourself. > - H. Norman Schwarzkopf > > > "Dave" <davefrick@newsgroup.nospam> wrote in message > news:e7l3XEQEHHA.4992@TK2MSFTNGP03.phx.gbl... >>I have a table that associates cars (vehicle ids) with attributes (color, >>body style, transmission) and I want to return a list of all cars that >>have certain attributes (e.g., a black car with manual transmission). >> >> The table looks like this... >> >> if object_id('tempdb..#attrib') IS NOT NULL >> DROP TABLE #attrib >> GO >> CREATE TABLE #attrib >> (veh_id int >> ,attrib varchar(12) >> ) >> GO >> INSERT #attrib (veh_id, attrib) VALUES(1, 'black') >> INSERT #attrib (veh_id, attrib) VALUES(1, '2 door') >> INSERT #attrib (veh_id, attrib) VALUES(1, 'manual') >> INSERT #attrib (veh_id, attrib) VALUES(2, 'red') >> INSERT #attrib (veh_id, attrib) VALUES(2, '4 door') >> INSERT #attrib (veh_id, attrib) VALUES(2, 'manual') >> >> --check >> SELECT * FROM #attrib >> >> >> --I want all the black cars with manual transmission.... >> >> --this returns no matches >> SELECT * >> FROM #attrib >> WHERE attrib='manual' and attrib='black' >> >> --this returns both cars >> SELECT distinct veh_id >> FROM #attrib >> WHERE attrib IN ('manual', 'black') >> >> --this returns both cars >> SELECT distinct veh_id >> FROM #attrib >> WHERE attrib ='manual' >> UNION >> SELECT distinct veh_id >> FROM #attrib >> WHERE attrib ='black' >> >> >> --this returns only the black car with a manual transmission >> SELECT distinct a1.veh_id >> FROM #attrib a1 JOIN >> (SELECT distinct veh_id >> FROM #attrib >> WHERE attrib ='black') As a2 >> ON a1.veh_id=a2.veh_id >> WHERE attrib ='manual' >> >> >> I could also write it as a subquery but is there a better approach? >> >> What if I want to find all black cars with manual trsnamissions that are >> 2 -doors? Then I have to add another join or subquery. I won't know >> ahead of time how many attributes the search will be on. >> >> Thanks for any suggestions. >> >> >> >> >> > >
Thanks Chris. That is a creative approach. [quoted text, click to view] "Chris Lim" <blackcap80@hotmail.com> wrote in message news:1164508664.395717.47760@h54g2000cwb.googlegroups.com... > Create a table to hold your search conditions like so: > > CREATE TABLE #search(attrib VARCHAR(12) NOT NULL) > INSERT #search VALUES('black') > INSERT #search VALUES('manual') > > Then you can compare the matches in your attributes table against the > number of rows in your search condition table: > > SELECT DISTINCT veh_id > FROM #attrib > WHERE > ( SELECT COUNT(*) > FROM #attrib a > INNER JOIN #search s > ON s.attrib = a.attrib > AND a.veh_id = #attrib.veh_id > ) > = ( SELECT COUNT(*) FROM #search) > > Chris > > Dave wrote: >> I have a table that associates cars (vehicle ids) with attributes (color, >> body style, transmission) and I want to return a list of all cars that >> have >> certain attributes (e.g., a black car with manual transmission). >> >> The table looks like this... >> >> if object_id('tempdb..#attrib') IS NOT NULL >> DROP TABLE #attrib >> GO >> CREATE TABLE #attrib >> (veh_id int >> ,attrib varchar(12) >> ) >> GO >> INSERT #attrib (veh_id, attrib) VALUES(1, 'black') >> INSERT #attrib (veh_id, attrib) VALUES(1, '2 door') >> INSERT #attrib (veh_id, attrib) VALUES(1, 'manual') >> INSERT #attrib (veh_id, attrib) VALUES(2, 'red') >> INSERT #attrib (veh_id, attrib) VALUES(2, '4 door') >> INSERT #attrib (veh_id, attrib) VALUES(2, 'manual') >> >> --check >> SELECT * FROM #attrib >> >> >> --I want all the black cars with manual transmission.... >> >> --this returns no matches >> SELECT * >> FROM #attrib >> WHERE attrib='manual' and attrib='black' >> >> --this returns both cars >> SELECT distinct veh_id >> FROM #attrib >> WHERE attrib IN ('manual', 'black') >> >> --this returns both cars >> SELECT distinct veh_id >> FROM #attrib >> WHERE attrib ='manual' >> UNION >> SELECT distinct veh_id >> FROM #attrib >> WHERE attrib ='black' >> >> >> --this returns only the black car with a manual transmission >> SELECT distinct a1.veh_id >> FROM #attrib a1 JOIN >> (SELECT distinct veh_id >> FROM #attrib >> WHERE attrib ='black') As a2 >> ON a1.veh_id=a2.veh_id >> WHERE attrib ='manual' >> >> >> I could also write it as a subquery but is there a better approach? >> >> What if I want to find all black cars with manual trsnamissions that are >> 2 -doors? Then I have to add another join or subquery. I won't know >> ahead >> of time how many attributes the search will be on. >> >> Thanks for any suggestions. >
[quoted text, click to view] Dave wrote: > Thanks Chris. That is a creative approach.
In case you're interested, this problem is an example of relational division. There's actually a better way to do it than the query I posted (this one doesn't require any subqueries): SELECT A.veh_id FROM #attrib A INNER JOIN #search S ON S.attrib = A.attrib GROUP BY A.veh_id HAVING COUNT(A.attrib) = (SELECT COUNT(attrib) FROM #search); See http://www.dbazine.com/ofinterest/oi-articles/celko1for an article on relational division.
Or SELECT DISTINCT veh_id FROM #attrib a WHERE NOT EXISTS (SELECT * FROM #search WHERE NOT EXISTS (SELECT * FROM #attrib AS a1 WHERE (a.attrib = a1.attrib) AND (a1.veh_id = #search.veh_id))); [quoted text, click to view] "Dave" <davefrick@newsgroup.nospam> wrote in message news:uJTzLxQEHHA.1012@TK2MSFTNGP04.phx.gbl... > Thanks Chris. That is a creative approach. > > > > "Chris Lim" <blackcap80@hotmail.com> wrote in message > news:1164508664.395717.47760@h54g2000cwb.googlegroups.com... >> Create a table to hold your search conditions like so: >> >> CREATE TABLE #search(attrib VARCHAR(12) NOT NULL) >> INSERT #search VALUES('black') >> INSERT #search VALUES('manual') >> >> Then you can compare the matches in your attributes table against the >> number of rows in your search condition table: >> >> SELECT DISTINCT veh_id >> FROM #attrib >> WHERE >> ( SELECT COUNT(*) >> FROM #attrib a >> INNER JOIN #search s >> ON s.attrib = a.attrib >> AND a.veh_id = #attrib.veh_id >> ) >> = ( SELECT COUNT(*) FROM #search) >> >> Chris >> >> Dave wrote: >>> I have a table that associates cars (vehicle ids) with attributes >>> (color, >>> body style, transmission) and I want to return a list of all cars that >>> have >>> certain attributes (e.g., a black car with manual transmission). >>> >>> The table looks like this... >>> >>> if object_id('tempdb..#attrib') IS NOT NULL >>> DROP TABLE #attrib >>> GO >>> CREATE TABLE #attrib >>> (veh_id int >>> ,attrib varchar(12) >>> ) >>> GO >>> INSERT #attrib (veh_id, attrib) VALUES(1, 'black') >>> INSERT #attrib (veh_id, attrib) VALUES(1, '2 door') >>> INSERT #attrib (veh_id, attrib) VALUES(1, 'manual') >>> INSERT #attrib (veh_id, attrib) VALUES(2, 'red') >>> INSERT #attrib (veh_id, attrib) VALUES(2, '4 door') >>> INSERT #attrib (veh_id, attrib) VALUES(2, 'manual') >>> >>> --check >>> SELECT * FROM #attrib >>> >>> >>> --I want all the black cars with manual transmission.... >>> >>> --this returns no matches >>> SELECT * >>> FROM #attrib >>> WHERE attrib='manual' and attrib='black' >>> >>> --this returns both cars >>> SELECT distinct veh_id >>> FROM #attrib >>> WHERE attrib IN ('manual', 'black') >>> >>> --this returns both cars >>> SELECT distinct veh_id >>> FROM #attrib >>> WHERE attrib ='manual' >>> UNION >>> SELECT distinct veh_id >>> FROM #attrib >>> WHERE attrib ='black' >>> >>> >>> --this returns only the black car with a manual transmission >>> SELECT distinct a1.veh_id >>> FROM #attrib a1 JOIN >>> (SELECT distinct veh_id >>> FROM #attrib >>> WHERE attrib ='black') As a2 >>> ON a1.veh_id=a2.veh_id >>> WHERE attrib ='manual' >>> >>> >>> I could also write it as a subquery but is there a better approach? >>> >>> What if I want to find all black cars with manual trsnamissions that are >>> 2 -doors? Then I have to add another join or subquery. I won't know >>> ahead >>> of time how many attributes the search will be on. >>> >>> Thanks for any suggestions. >> > >
On Sat, 25 Nov 2006 19:08:56 -0800, "Dave" [quoted text, click to view] <davefrick@newsgroup.nospam> wrote: >I have a table that associates cars (vehicle ids) with attributes (color, >body style, transmission) and I want to return a list of all cars that have >certain attributes (e.g., a black car with manual transmission). > >The table looks like this... > >if object_id('tempdb..#attrib') IS NOT NULL >DROP TABLE #attrib >GO >CREATE TABLE #attrib > (veh_id int > ,attrib varchar(12) > ) >GO >INSERT #attrib (veh_id, attrib) VALUES(1, 'black') >INSERT #attrib (veh_id, attrib) VALUES(1, '2 door') >INSERT #attrib (veh_id, attrib) VALUES(1, 'manual') >INSERT #attrib (veh_id, attrib) VALUES(2, 'red') >INSERT #attrib (veh_id, attrib) VALUES(2, '4 door') >INSERT #attrib (veh_id, attrib) VALUES(2, 'manual') > >--check >SELECT * FROM #attrib > > >--I want all the black cars with manual transmission....
select * into #candidate from #attrib where attrib in ('black','manual') --- if the arity fits, you've got a hit select veh_id from #candidate group by veh_id having count(*)=2
Don't see what you're looking for? Try a search.
|