【问题标题】:How to find exact match of a set of values?如何找到一组值的完全匹配?
【发布时间】:2022-01-28 11:22:43
【问题描述】:

我有一个简单的表格来存储师生关系,以显示学生在上课或老师在教谁。无论哪种方式。 (为便于阅读,按老师排序)

CREATE TABLE TS_RELATIONSHIP
(
    Teacher NVARCHAR(10);
    Student NVARCHAR(10);
)

例子:

Teacher Student
-------------------
Conner     Yumi
Conner     Shawn
Conner     Casey
Ericson    Eric
Ericson    Yumi
Ericson    Sue
Ericson    Lin
Johnson    Shawn
Johnson    Lin
Johnson    Ivan
Johnson    Casey
Johnson    Gina

现在,如果我有一组学生姓名,如何找到数据的“完全匹配”? (顺序无所谓)

例如,如果学生姓名的集合是Casey, Gina, Ivan, Lin, Shawn,那么我知道Johnson 是我要找的老师,除了这五个人以外的任何人(正好这五个,不是一个学生短或更多) , 不会返回任何内容(除非其他老师得到完全匹配)。

我尝试在 C# 的帮助下做到这一点,并做了类似的事情,

string[] studentNames;
DataTable dt = SQL.GetDT(@"SELECT Teacher FROM [TS_RELATIONSHIP] WHERE Student=@p0;",studentNames[0]);    //Assuming SQL.GetDT is a function that execute the T-SQL and return it as a DataTable.
for(int i=1;i<studentNames.Length;i++)
{
    string teacherNames = string.Join(",", dt.AsEnumerable().Select(x => string.Format($"'{x.Field<string>("Teacher")}'")));
    dt=SQL.GetDT(@"SELECT Teacher FROM [TS_RELATIONSHIP] WHERE Student=@p0 and Teacher IN (@p1);",studentNames[i], teacherNames);
}

但很快就会发现这个方法不会返回“精确”匹配!

只要教师“包含”学生列表,它就会作为答案返回(这是不正确的)。

有人能这么好心教我怎么做吗?

纯 T-SQL 或借助 C# 都可以。

非常感谢您的帮助!

【问题讨论】:

  • 据我了解,您希望找到拥有相同学生的老师,反之亦然?
  • 另外,您能否在问题中添加,具体希望收到的示例。
  • @kanils_ 是的。例如,输入:学生姓名列表(Casey、Gina、Ivan、Lin、Shawn),输出:Johnson。

标签: c# tsql


【解决方案1】:

这是一个称为Relational Division Without Remainder 的问题。有多种解决方案。这是一个:

首先,您必须将所有值作为表传递给 SQL,为此您需要一个表值参数(如下所示的 C#)。

我将假设 TS_RELATIONSHIP 在两列中是唯一的,并且学生的输入列表也是唯一的

SELECT
  ts.Teacher
FROM TS_RELATIONSHIP ts
LEFT JOIN @students s ON s.Value = ts.Student
GROUP BY
  ts.Teacher
HAVING COUNT(*) = @studentCount
   AND COUNT(s.Value) = @studentCount;

这样做是从表中取出所有行,将其左连接到输入列表,按Teacher 对其进行分组,并确保总行数等于输入总数,并且匹配输入的数量也完全等于该总数。

你需要创建一个表格类型,我通常有几个标准的单列使用:

CREATE TYPE dbo.StringList AS TABLE (Value nvarchar(250) NOT NULL PRIMARY KEY);

要将完整列表作为 TVP 传递,您可以执行以下操作:

var dtInput = new DataTable();
dtInput.Columns.Add("Value", typeof(string));
foreach (var student in studentNames)
    dtInput.Rows.Add(student);

var dtResult = new DataTable();
using (var conn = new SqlConnection(yourConnString))
using (var comm = new SqlCommand("THE ABOVE QUERY"))
{
    comm.Parameters.Add("@studentCount", SqlDbType.Int).Value = studentNames.Length;
    comm.Parameters.Add(new SqlParameter("@students", SqlDbType.Structured) { TypeName = "dbo.StringList", Direction = ParameterDirection.Input, Value = dtInput});

    conn.Open();
    using (var reader = comm.ExecuteReader())
        dtResult.Load(reader);
}

【讨论】:

    【解决方案2】:

    这可能是一个解决方案:

    给定这个类用于关系列表

         private class Nrelation
        {
            public string T;
            public string S;
        }
    

    这应该可以工作

        private string DOTest(List<string> match) 
        {
            List<Nrelation> testList = new List<Nrelation>(); //<--Populate your relation data here
    
            return testList.GroupBy(g => g.T).ToList()
              .Where(item => isEqual(item.Select(x => x.S).ToList(), match))
              .FirstOrDefault().Key;
    
        }
    
        private bool isEqual(List<string> list1, List<string> list2) 
        { 
            return Enumerable.SequenceEqual(list1.OrderBy(e => e), list2.OrderBy(e => e));
        }
    

    【讨论】:

      猜你喜欢
      • 2015-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-08
      • 2013-05-21
      • 1970-01-01
      相关资源
      最近更新 更多