Author |
Topic |
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-07-03 : 07:53:15
|
I am currently developing an application for the Swedish Military. It is classified so I will use the concept of an dating service instead when explaining here.It is still the same I want, but more elegant. The queries I have today are ugly hacks. In real application I search for conflict of interests (COI), but here I will call it a match instead.To this date, I have managed to narrow down the three queries to 1. Insert into table variable @Coi 2. Update the table variable @Coi 3. Select from table variable @CoiI believe this can be done more effeciently and more elegant. Is anyone up for the challenge? khtan, Madhivanan, RyanRandall, Michael Valentine Jones, Jen, Kristen, nr?The task is to present all matches (COIs) against all members in the table. All members belong to a specific level, depending on ranks. For dating service this could be Beginner, Novice, Intermediate and so on. In my application it is Lieutenant, Captain, Major, Colonel and so on. The anwers given are not mandatory. Every member can choose any number of answers within category, if at all.The task is to create a query that select all matches (COIs) between the member that has at least one match within any category. The minimum number of categories is determined by table MemberLevels. If a member belongs to memberlevel 7, the two minimum categories match could be any two categories - Looks and Wealth, or Interests and Marriage, or Children and Living, that the two matching members has given the same answer. Minimum number of categories is as specified in MemberLevels table.Good luck!This is an extract of the DDL for relevant tables,CREATE TABLE [dbo].[Alternatives] ( [AlternativeID] [int] IDENTITY (1, 1) NOT NULL , [CategoryID] [int] NOT NULL , [AlternativeText] [varchar] (50) COLLATE Finnish_Swedish_CI_AS NOT NULL ) ON [PRIMARY]GOCREATE TABLE [dbo].[Answers] ( [AnswerID] [int] IDENTITY (1, 1) NOT NULL , [MemberID] [int] NOT NULL , [AlternativeID] [int] NOT NULL ) ON [PRIMARY]GOCREATE TABLE [dbo].[Categories] ( [CategoryID] [int] IDENTITY (1, 1) NOT NULL , [CategoryName] [varchar] (50) COLLATE Finnish_Swedish_CI_AS NOT NULL ) ON [PRIMARY]GOCREATE TABLE [dbo].[MemberLevels] ( [Level] [int] IDENTITY (1, 1) NOT NULL , [Categories] [int] NOT NULL ) ON [PRIMARY]GOCREATE TABLE [dbo].[Members] ( [MemberID] [int] IDENTITY (1, 1) NOT NULL , [MemberName] [varchar] (50) COLLATE Finnish_Swedish_CI_AS NOT NULL , [MemberLevel] [int] NOT NULL ) ON [PRIMARY]GOALTER TABLE [dbo].[Alternatives] WITH NOCHECK ADD CONSTRAINT [PK_Alternative] PRIMARY KEY CLUSTERED ( [AlternativeID] ) ON [PRIMARY] GOALTER TABLE [dbo].[Categories] WITH NOCHECK ADD CONSTRAINT [PK_Categories] PRIMARY KEY CLUSTERED ( [CategoryID] ) ON [PRIMARY] GOALTER TABLE [dbo].[MemberLevels] WITH NOCHECK ADD CONSTRAINT [PK_MemberLevels] PRIMARY KEY CLUSTERED ( [Level] ) ON [PRIMARY] GOALTER TABLE [dbo].[Members] WITH NOCHECK ADD CONSTRAINT [PK_Members] PRIMARY KEY CLUSTERED ( [MemberID] ) ON [PRIMARY] GOALTER TABLE [dbo].[Alternatives] ADD CONSTRAINT [IX_Alternatives] UNIQUE NONCLUSTERED ( [AlternativeID], [CategoryID] ) ON [PRIMARY] GOALTER TABLE [dbo].[Alternatives] ADD CONSTRAINT [FK_Alternatives_Categories] FOREIGN KEY ( [CategoryID] ) REFERENCES [dbo].[Categories] ( [CategoryID] )GOALTER TABLE [dbo].[Answers] ADD CONSTRAINT [FK_Answers_Alternatives] FOREIGN KEY ( [AlternativeID] ) REFERENCES [dbo].[Alternatives] ( [AlternativeID] ), CONSTRAINT [FK_Answers_Members] FOREIGN KEY ( [MemberID] ) REFERENCES [dbo].[Members] ( [MemberID] )GOALTER TABLE [dbo].[Members] ADD CONSTRAINT [FK_Members_MemberLevels] FOREIGN KEY ( [MemberLevel] ) REFERENCES [dbo].[MemberLevels] ( [Level] )GO but for the ease of the challenge I have converted the sample data to table variables instead.declare @answers table (AnswerID int, MemberID int, AlternativeID int)insert @answersselect 1, 1000, 29 union allselect 2, 1000, 5 union allselect 3, 1000, 4 union allselect 4, 1000, 7 union allselect 5, 1000, 13 union allselect 6, 1000, 14 union allselect 7, 1000, 20 union allselect 8, 1000, 22 union allselect 9, 1000, 27 union allselect 10, 2000, 1 union allselect 11, 2000, 10 union allselect 12, 2000, 4 union allselect 13, 2000, 7 union allselect 14, 2000, 13 union allselect 15, 2000, 14 union allselect 16, 2000, 18 union allselect 17, 2000, 23 union allselect 18, 2000, 27 union allselect 19, 3000, 9 union allselect 20, 3000, 2 union allselect 21, 3000, 3 union allselect 22, 3000, 11 union allselect 23, 3000, 6 union allselect 24, 3000, 16 union allselect 25, 3000, 17 union allselect 26, 3000, 22 union allselect 27, 3000, 25 union allselect 28, 4000, 29 union allselect 29, 4000, 1 union allselect 30, 4000, 4 union allselect 31, 4000, 8 union allselect 32, 4000, 15 union allselect 33, 4000, 19 union allselect 34, 4000, 21 union allselect 35, 4000, 28 union allselect 36, 5000, 2 union allselect 37, 5000, 4 union allselect 38, 5000, 8 union allselect 39, 5000, 13 union allselect 40, 5000, 14 union allselect 41, 5000, 15 union allselect 42, 5000, 20 union allselect 43, 5000, 24 union allselect 44, 5000, 26 union allselect 45, 6000, 29 union allselect 46, 6000, 5 union allselect 47, 6000, 7 union allselect 48, 5000, 7 union allselect 49, 6000, 16 union allselect 50, 6000, 17 union allselect 51, 6000, 19 union allselect 52, 6000, 21 union allselect 53, 6000, 23 union allselect 54, 7000, 1 union allselect 55, 7000, 9 union allselect 56, 7000, 12 union allselect 57, 7000, 15 union allselect 58, 7000, 22 union allselect 59, 7000, 27declare @members table (MemberID int, MemberName varchar(50), MemberLevel int)insert @membersselect 1000, 'Peter', 4 union allselect 2000, 'Jennie', 4 union allselect 3000, 'Jane', 7 union allselect 4000, 'John', 1 union allselect 5000, 'Henrik', 1 union allselect 6000, 'Sara', 2 union allselect 7000, 'Anette', 7declare @memberlevels table (Level int, Categories int)insert @memberlevelsselect 1, 2 union allselect 2, 1 union allselect 3, 1 union allselect 4, 7 union allselect 5, 1 union allselect 6, 1 union allselect 7, 2declare @categories table (CategoryID int, CategoryName varchar(50))insert @categoriesselect 1, 'Looks' union allselect 2, 'Wealth' union allselect 3, 'Health' union allselect 4, 'Interests' union allselect 5, 'Children' union allselect 6, 'Marriage' union allselect 7, 'Living'declare @alternatives table (AlternativeID int, CategoryID int, AlternativeText varchar(50))insert @alternativesselect 1, 1, 'Thin' union allselect 2, 1, 'Thick' union allselect 3, 2, 'Poor' union allselect 4, 2, 'Rich' union allselect 5, 2, 'Independent' union allselect 6, 3, 'Astma' union allselect 7, 3, 'No known illnesses' union allselect 8, 3, 'Beer belly' union allselect 9, 1, 'Tall' union allselect 10, 1, 'Short' union allselect 11, 2, 'Welfare' union allselect 12, 3, 'Smoker' union allselect 13, 4, 'Film' union allselect 14, 4, 'Movies' union allselect 15, 4, 'Walk in the park' union allselect 16, 4, 'Gym' union allselect 17, 5, 'Not in my lifetime' union allselect 18, 5, 'None. But want to have' union allselect 19, 5, 'Have. But no more' union allselect 20, 5, 'Have. Want more' union allselect 21, 6, 'If you want to...' union allselect 22, 6, 'Already is' union allselect 23, 6, 'Within next three years' union allselect 24, 6, 'Nothing for me' union allselect 25, 7, 'Rented apartment' union allselect 26, 7, 'Rented house' union allselect 27, 7, 'Bought apartment' union allselect 28, 7, 'Bought house' union allselect 29, 1, 'Medium built' Output from query should be something likeMemID Name MemID Name CatID CatName AnsID AnsText------ ----- ----- ------ ----- --------- ----- ------------------ 1000 Peter 2000 Jennie 1 Looks 4 Rich 1000 Peter 2000 Jennie 7 Living 7 No known illnesses 1000 Peter 2000 Jennie 4 Interests 13 Film 1000 Peter 2000 Jennie 4 Interests 14 Movies produced by all matches/conflicts present. Even if minimum level is 2, all matches/COIs should be output, if there are more.Peter LarssonHelsingborg, Sweden |
|
Kristen
Test
22859 Posts |
Posted - 2006-07-03 : 09:32:47
|
"I am currently developing an application for the Swedish Military. It is classified so I will use the concept of an dating service instead when explaining here."I have built a dating site database ... but its proprietary so I'll have to charge you to convert it for use by the Swedish Military We use two tables - Self and Wants.You describe what you have (Self) and what you want in your partner (wants). You can be vague, or broad. Your answers are stored as bit patterns - so if you are 30 (one bit for, say, 28-32) and you are looking for someone 25-27, 28-32 or 33-35 then that sets 3 bits. Just OR'ing them together tells you if you have a match, or not.We have about 10 columns for the bit patterns, each column represents one question and its answers - so columns for Age, Profession type, Education level, Weight and so on.The search is not very efficient! but it does the job, and people do not change their characteristics very often - other than new registrations, so we do cache some data to ease the processing burden.Kristen |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-07-03 : 09:42:18
|
Thanks for the swift reply, Kristen.But this doesn't do the job for me. The application has to comply for BELBIN project model ([url]http://www.belbin.com/downloads/Belbin_Team_Role_Summary_Descriptions.pdf[/url]). It's purpose is to set together certain teams. It could be foreign service task force, or in-house project. Assessments of each member are changed at at least 4 times per year, based on interviews with subordinates, higher officers and training.Today, my three queries runs for 15 seconds for 31,000 members and approx 2.4 million answers. And I think it the ugly hacks will be hard to maintain in the future. So I need a more elegant solution. In the queries below there is also a version component, since we must maintain all assessments quarterly. The reason I choose version, is that the version is not always quarterly. Sometimes it can be monthly, and sometimes only twice a year. But there is a need to compare every persons assessments, for every period, in order to see if a person advances in his/hers people skills, technical performance and so on.These are the three queries I have todayDECLARE @Coi TABLE ( RuleID INT, RuleSet INT, TargetModelID INT, TargetMemberCode VARCHAR(30), TargetMemberName VARCHAR(120), TargetDimensionID INT, TargetDimensionName VARCHAR(32), SourceModelID INT, SourceMemberCode VARCHAR(30), SourceMemberName VARCHAR(120), SourceDimensionID INT, SourceDimensionName VARCHAR(32), Cloned TINYINT )INSERT INTO @Coi ( RuleID, RuleSet, TargetModelID, TargetMemberCode, TargetMemberName, TargetDimensionID, TargetDimensionName, SourceModelID, SourceMemberCode, SourceMemberName, SourceDimensionID, SourceDimensionName, Cloned )SELECT Rules.RuleID, Rules.RuleSet, Rules.ModelID, TargetModels.MemberCode, TargetModels.MemberName, TargetDimensions.DimensionID, TargetDimensions.DimensionName, Translations.ModelID, SourceModels.MemberCode, SourceModels.MemberName, SourceDimensions.DimensionID, SourceDimensions.DimensionName, 0FROM RulesINNER JOIN Models TargetModels ON TargetModels.ModelID = Rules.ModelIDINNER JOIN Dimensions TargetDimensions ON TargetDimensions.DimensionID = TargetModels.DimensionIDINNER JOIN Translations ON Translations.RuleID = Rules.RuleIDINNER JOIN Models SourceModels ON SourceModels.ModelID = Translations.ModelIDINNER JOIN Dimensions SourceDimensions ON SourceDimensions.DimensionID = SourceModels.DimensionIDINNER JOIN Prioritygroups ON Prioritygroups.PrioritygroupID = Rules.PrioritygroupIDWHERE Prioritygroups.VersionID = @VersionID AND Prioritygroups.PrioritygroupLevel = @PrioritygroupLevelUPDATE r1SET r1.Cloned = 1FROM @Coi r1, @Coi r2WHERE r1.SourceModelID = r2.SourceModelID AND r1.RuleID <> r2.RuleIDSELECT DISTINCT coi.RuleID, r.RuleID, coi.SourceDimensionIDFROM @Coi coiCROSS JOIN ( SELECT RuleID, SourceDimensionID FROM @coi ) RWHERE coi.RuleID < R.RuleID AND coi.SourceDimensionID = R.SourceDimensionID AND coi.Cloned = 1ORDER BY coi.RuleID, R.RuleID, coi.SourceDimensionID Peter LarssonHelsingborg, Sweden |
 |
|
rob_farley
Yak Posting Veteran
64 Posts |
Posted - 2006-07-04 : 05:00:28
|
Man, I want to be able to look at this and find a solution, but I have to go home... maybe another day.Rob Farleyhttp://robfarley.blogspot.com |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-07-04 : 05:44:50
|
I have managed to slim the queries. Now I have got rid of the update part.So now there is only to queries1. Insert @Coi2. Select @CoiI hope this can be slimmed further into only one SELECT.This is what I got this fardeclare @Coi table (AltID INT, CatID INT, FromID INT, Cats INT, ToID INT)insert @Coiselect ans1.alternativeid altid, alt1.categoryid catid, mem1.memberid fromid, lev1.categories cats, mem2.memberid toidfrom @answers ans1inner join @answers ans2 on ans2.alternativeid = ans1.alternativeid and ans2.memberid <> ans1.memberidinner join @alternatives alt1 on alt1.alternativeid = ans1.alternativeidinner join @members mem1 on mem1.memberid= ans1.memberidinner join @members mem2 on mem2.memberid= ans2.memberidinner join @memberlevels lev1 on lev1.level = mem1.memberlevelinner join @memberlevels lev2 on lev2.level = mem2.memberlevelwhere lev1.categories <= lev2.categoriesselect c.fromid, mem1.membername, c.toid, mem2.membername, c.catid, cat.categoryname, c.altid, alt.alternativetext alttextfrom @coi cinner join @members mem1 on mem1.memberid = c.fromidinner join @members mem2 on mem2.memberid = c.toidinner join @categories cat on cat.categoryid = c.catidinner join @alternatives alt on alt.alternativeid = c.altidinner join ( select fromid, toid from @coi group by fromid, toid having count(distinct catid) >= min(cats) ) z on z.fromid = c.fromid and z.toid = c.toidorder by c.fromid, c.toid Peter LarssonHelsingborg, Sweden |
 |
|
Seventhnight
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2006-07-05 : 11:33:04
|
You could probably do this is one query, but it isn't worth the effort. It would probably run slower anyway.this is my shot:Declare @Prep Table (MemIdA int, MemNameA varchar(20), MemIdB int, MemNameB varchar(20), CatId int, CatName varchar(20), AnsId int, AnsText varchar(50), matches int)Insert Into @PrepSelect MemIdA = A.MemberId, MemNameA = A.MemberName, MemIdB = B.MemberId, MemNameB = B.MemberName, CatId = C.CategoryId, CatName = D.CategoryName, AnsId = C.AlternativeId, AnsText = C.AlternativeText, MinMatches = (Select min(categories) From @memberlevels Where Level in (A.memberLevel,B.memberLevel))From @members AInner Join @answers A1On A.memberId = A1.memberIdInner Join @answers B1On A1.AlternativeId = B1.AlternativeIdand A1.answerId <> B1.answerIdInner Join @members BOn B1.memberId = B.memberIdand A.memberId < B.memberIdInner Join @alternatives COn A1.AlternativeId = C.AlternativeIdInner Join @categories DOn C.CategoryId = D.CategoryIdSelect * From @Prep Where MemIdA = 1000 and MemIdB = 2000Select * From @Prep ZInner Join ( Select MemIdA, MemIdB From @Prep A Group By MemIdA, MemIdB Having count(CatId) >= min(matches) ) YOn Z.MemIdA = Y.MemIdAand Z.MemIdB = Y.MemIdB Corey Co-worker on children "...when I have children, I'm going to beat them. Not because their bad, but becuase I think it would be fun ..." |
 |
|
|
|
|