SQL Server:有效地在多对多列中搜索许多值?

时间:2015-10-07 14:39:33

标签: sql sql-server

我正在使用SQL Server创建一个网站。在管理界面中,我有两个字段:

  • 主题:数学,英语,历史......
  • 等级:1,2,3,4 ......

可以为记录分配多个字段值。

现在在前端搜索中,我希望访问者能够选择一个以上的字段值进行搜索。例如,某人可能会搜索主题为数学或历史,并且成绩为1或3。

我应该使用哪种表设计和SQL语句(或MS专有语句)来进行有效搜索?

谢谢和问候。

更新

感谢所有输入!

我觉得有必要解释一下。我技术熟悉并熟悉SQL。我在多年的编程经验中学到的一件事是实用。对于这个问题,我已经有了初步设计,但不确定其他人如何处理高效搜索(总有更聪明的人)。这是我用于存储记录的表格设计:

Subject
type: varchar. record example: ,1,3, (each is the id of corresponding value)

Grade (this means applicable grade)
type: varchar. record example: ,1,2, (each is the id of corresponding values. this means a record is applicable to grade 1, 2)

搜索示例

where (subject LIKE  '%,1,%' OR subject like '%,3,%') AND (grade like '%,1,%')

这种设计应该导致有效的搜索,但缺点是它增加了后端的复杂性数据管理。

我的设计的另一个原因是:主题和成绩每个都有一个从不/很少改变的值列表,一旦创建了一个记录,它就永远或很少更新。

我试图在简单性,可理解性,设计,管理等方面取得平衡。

1 个答案:

答案 0 :(得分:1)

create table Subject (
    SubjectId int identity(1, 1),
    SubjectName nvarchar(255),
    other fields.... )

create table GradingScale (
    GradeId int identity(1, 1),
    Grade int,
    Description varchar(25),
    other fields... )

create table Students (
    StudentId int identity(1, 1),
    StudentName nvarchar(255))

create table StudentGrades (
    StudentId int,
    SubjectId int,
    GradeId int,
    SemesterId int )

create FUNCTION [dbo].[fnArray] ( @Str varchar(max), @Delim varchar(1) = ' ', @RemoveDups bit = 0 )
returns  @tmpTable table ( arrValue varchar(max))
as
begin
   declare @pos integer
   declare @lastpos integer
   declare @arrdata varchar(8000)
   declare @data varchar(max)

   set @arrdata = replace(@Str,@Delim,'|')
   set @arrdata = @arrdata + '|'
   set @lastpos = 1
   set @pos = 0
   set @pos = charindex('|', @arrdata)
   while @pos <= len(@arrdata) and @pos <> 0
   begin
      set @data = substring(@arrdata, @lastpos, (@pos - @lastpos))
      if rtrim(ltrim(@data)) > ''
      begin
         if @RemoveDups = 0
         begin          
            insert into @tmpTable ( arrValue ) values ( @data )
         end
         else
         begin
             if not exists( select top 1 arrValue from @tmpTable where arrValue = @data ) 
             begin   
                insert into @tmpTable ( arrValue ) values ( @data )
             end
        end
      end
      set @lastpos = @pos + 1
      set @pos = charindex('|', @arrdata, @lastpos)
   end
   return 
end

select *
  from Students st
 inner join StudentGrades sg on sg.StudentId = st.StudentId
 inner join Subject s on sg.SubjectId = s.SubjectId
 inner join GradingScale gs on sg.GradeId = gs.GradeId
 inner join dbo.fnArray(@subjects, ',') sArr on s.SubjectId = convert(int, sArr.arrValue)
 inner join dbo.fnArray(@grades, ',') gArr on gs.GradeId = convert(int, gArr.arrValue)

显然@subjectId和@gradeId可以通过一些下拉选择器传入,或者你的UI是由组成的。

编辑使用dbo.fnArray,这是一个可以将分隔的字符串解析为列表的小工具。

现在当然这意味着如果你有2个科目和2个成绩......比如给我看所有学生(数学和科学)和得分(2或3),这将有效。但是,如果您想要学习数学并且得分为2或3的学生或者学习科学并且得分为3的学生则必须重新编写查询。