如何找到一组值的精确匹配?
How to find exact match of a set of values?
我有一个简单的 table 存储师生关系,以显示 class 学生在上课或老师在教谁。无论哪种方式。 (为了便于阅读,由老师排序)
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
就是我要找的老师,除了这五个人(就是这五个,不是一个学生短或更多),什么都不会 returned(除非其他老师得到完全匹配)。
我尝试在 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);
}
但很快就会发现此方法不会return“精确”匹配!
只要教师“包含”学生列表,就会return编辑为答案(这是不正确的)。
有人可以教我如何正确地做吗?
使用纯 T-SQL 或借助 C# 都可以。
非常感谢您的帮助!
这是一个被称为Relational Division Without Remainder的问题。有许多解决方案。这是一个:
首先,您必须将所有值作为 table 传递给 SQL,为此您需要一个 Table 赋值参数(如下所示的 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;
这样做是从 table 中取出所有行,将其左连接到输入列表,按 Teacher
分组并确保总行数等于输入的总数,并且匹配的输入的数量也正好等于该总数。
你需要创建一个Table类型,我通常有几个标准的单列类型:
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);
}
这可能是一个解决方案:
鉴于此 class 关系列表
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));
}
我有一个简单的 table 存储师生关系,以显示 class 学生在上课或老师在教谁。无论哪种方式。 (为了便于阅读,由老师排序)
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
就是我要找的老师,除了这五个人(就是这五个,不是一个学生短或更多),什么都不会 returned(除非其他老师得到完全匹配)。
我尝试在 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);
}
但很快就会发现此方法不会return“精确”匹配!
只要教师“包含”学生列表,就会return编辑为答案(这是不正确的)。
有人可以教我如何正确地做吗?
使用纯 T-SQL 或借助 C# 都可以。
非常感谢您的帮助!
这是一个被称为Relational Division Without Remainder的问题。有许多解决方案。这是一个:
首先,您必须将所有值作为 table 传递给 SQL,为此您需要一个 Table 赋值参数(如下所示的 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;
这样做是从 table 中取出所有行,将其左连接到输入列表,按 Teacher
分组并确保总行数等于输入的总数,并且匹配的输入的数量也正好等于该总数。
你需要创建一个Table类型,我通常有几个标准的单列类型:
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);
}
这可能是一个解决方案:
鉴于此 class 关系列表
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));
}