SQL 查询根据值和字段名从两个表中获取计数
SQL query to obtain counts from two tables based on values and field names
我想按区统计考生的提醒
以下是分区警报查找table
Table_LKP_AlertMastInfo
DistrictID FieldName AlertOptionValue
71 AreYouMarried Yes
71 Gender Female
72 AreYouMarried Yes
上面的 Table_LKP_AlertMastInfo FieldName 应该与 table_RegistrationInfo 字段进行比较以检查 AlertOptionValue 以获得计数。
以下是候选人的详细信息table:
Table_RegistrationInfo
CandidateId DistrictID AreYouMarried Gender
Can001 71 Yes Female
Can002 71 No Female
Can003 72 Yes Man
Can004 72 No Man
我想要如下输出:
Can001 2
Can002 1
Can003 1
以上输出计数的解释:
Can001 have selected AreYouMarried:Yes and Gender:Female then count value 2
Can002 have selected Gender:Female then count value 1
Can003 have selected AreYouMarried:Yes then count value 1
Can004 have not alerts
我设法在不使用动态查询的情况下获得了预期的结果。
不确定这是否是您要查找的内容:
SELECT DISTINCT
c.CandidateId, SUM(a.AreYouMarriedAlert + a.GenderAlter) AS AlterCount
FROM
Table_RegistrationInfo c
OUTER APPLY
(
SELECT
CASE
WHEN a.FieldName = 'AreYouMarried' AND c.AreYouMarried = a.AlertOptionValue THEN 1
ELSE 0
END AS AreYouMarriedAlert,
CASE
WHEN a.FieldName = 'Gender' AND c.Gender = a.AlertOptionValue THEN 1
ELSE 0
END AS GenderAlter
FROM
Table_LKP_AlertMastInfo a
WHERE
a.DistrictID = c.DistrictID
) a
GROUP BY c.CandidateId
HAVING SUM(a.AreYouMarriedAlert + a.GenderAlter) > 0
结果:
这是一种简单的方法:
SELECT subq.*
FROM
(SELECT CandidateId,
(SELECT COUNT(*)
FROM Table_LKP_AlertMastInfo ami
WHERE ami.DistrictID = ri.DistrictID
AND ami.FieldName ='AreYouMarried'
AND ami.AlertOptionValue = ri.AreYouMarried) +
(SELECT COUNT(*)
FROM Table_LKP_AlertMastInfo ami
WHERE ami.DistrictID = ri.DistrictID
AND ami.FieldName ='Gender'
AND ami.AlertOptionValue = ri.Gender) AS [count]
FROM Table_RegistrationInfo ri) subq
WHERE subq.[count] > 0;
我假设有 100 个字段,您有一组警报,这些警报是值的组合。此外,我假设您可以始终以正确的顺序拥有一个 select 列表。所以
select candidateid,
AreyouMarried || '|' || Gender all_responses_in_one_string
from ....
是可能的。所以上面会 return
candidateid all_responses_in_one_string
can001 Yes|Female
can002 No|Female
现在您的警报可以是串联字符串的正则表达式。您的警报基于您匹配的数量。
我不确定这是否可以使用 SQL 完全完成。如果您正在使用一些后端技术,例如 ADO.NET,那么您可以将结果存储在 Datatables 中。遍历列名并进行比较。
动态 SQL 可用于使 Table_LKP_AlertMastInfo 看起来像 Table_RegistrationInfo。
此脚本可用于存储过程,结果可在数据表中检索。
DECLARE @SQL NVARCHAR(MAX)
DECLARE @PivotFieldNameList nvarchar(MAX)
SET @SQL = ''
SET @PivotFieldNameList = ''
SELECT @PivotFieldNameList = @PivotFieldNameList + FieldName + ', '
FROM (SELECT DISTINCT FieldName FROM Table_LKP_AlertMastInfo) S
SET @PivotFieldNameList = SUBSTRING(@PivotFieldNameList, 1, LEN(@PivotFieldNameList) - 1)
--SELECT @PivotFieldNameList
SET @SQL = ' SELECT DistrictId, ' + @PivotFieldNameList + ' FROM
Table_LKP_AlertMastInfo
PIVOT
( MAX(AlertOptionValue)
FOR FieldName IN (' + @PivotFieldNameList + '
) ) AS p '
PRINT @SQL
EXEC(@SQL)
以上查询结果如下
DistrictId AreYouMarried Gender
71 Yes Female
72 Yes NULL
如果你从Table_RegistrationInfo中得到结果到另一个数据表中,那么两者都可以用于比较。
如果您的数据是按原样建模的,即 Table_LKP_AlertMastInfo
中的键值对和 Table_RegistrationInfo
中的列,那么如果没有动态 SQL,这将是不可能的。因此,让我们开始吧。提供您需要的确切结果的存储过程的完整代码在最后,我将在后面解释它的作用。
因为警报被指定为键值对(字段名-字段值),我们首先需要以相同的格式获取候选数据。 UNPIVOT
可以解决这个问题,如果我们可以得到它的字段列表。如果我们只有你在问题中提到的两个字段,那会很容易,比如:
SELECT CandidateId, DistrictID
, FieldName
, FieldValue
FROM Table_RegistrationInfo t
UNPIVOT (FieldValue FOR FieldName IN (AreYouMarried, Gender)) upvt
当然不是这样,所以我们需要动态 select 我们感兴趣的字段列表并提供它。由于您使用的是 2008 R2,STRING_AGG is not yet available, so we'll use the XML trick to aggregate all the fields into a single string 并将其提供给上面的查询。
DECLARE @sql NVARCHAR(MAX)
SELECT @sql = CONCAT('SELECT CandidateId, DistrictID
, FieldName
, FieldValue
FROM Table_RegistrationInfo t
UNPIVOT (FieldValue FOR FieldName IN (',
STUFF((
SELECT DISTINCT ',' + ami.FieldName
FROM Table_LKP_AlertMastInfo ami
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''), ')) upvt')
PRINT @sql
这产生的输出几乎与我编写的查询完全一样。接下来,我们需要将这些数据存储在某个地方。临时 tables 来救援。让我们创建一个并使用此动态 SQL.
插入其中
CREATE TABLE #candidateFields
(
CandidateID VARCHAR(50),
DistrictID INT,
FieldName NVARCHAR(200),
FieldValue NVARCHAR(1000)
);
INSERT INTO #candidateFields
EXEC sp_executesql @sql
-- (8 rows affected)
-- We could index this for good measure
CREATE UNIQUE CLUSTERED INDEX uxc#candidateFields on #candidateFields
(
CandidateId, DistrictId, FieldName, FieldValue
);
太好了,有了这个,我们现在拥有相同格式的两个数据集 - 警报和候选数据。这是一个加入以找到两者之间匹配的问题:
SELECT cf.CandidateID, COUNT(*) AS matches
FROM #candidateFields cf
INNER
JOIN Table_LKP_AlertMastInfo alerts
ON alerts.DistrictID = cf.DistrictID
AND alerts.FieldName = cf.FieldName
AND alerts.AlertOptionValue = cf.FieldValue
GROUP BY cf.CandidateID
为示例数据提供所需的输出:
CandidateID matches
-------------------------------------------------- -----------
Can001 2
Can002 1
Can003 1
(3 rows affected)
所以我们现在可以将所有这些拼接在一起以形成一个可重用的存储过程:
CREATE PROCEDURE dbo.findMatches
AS
BEGIN
SET NOCOUNT ON;
DECLARE @sql NVARCHAR(MAX)
SELECT @sql = CONCAT('SELECT CandidateId, DistrictID
, FieldName
, FieldValue
FROM Table_RegistrationInfo t
UNPIVOT (FieldValue FOR FieldName IN (',
STUFF((
SELECT DISTINCT ',' + ami.FieldName
FROM Table_LKP_AlertMastInfo ami
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''), ')) upvt')
CREATE TABLE #candidateFields
(
CandidateID VARCHAR(50),
DistrictID INT,
FieldName NVARCHAR(200),
FieldValue NVARCHAR(1000)
);
INSERT INTO #candidateFields
EXEC sp_executesql @sql
CREATE UNIQUE CLUSTERED INDEX uxc#candidateFields on #candidateFields
(
CandidateId, DistrictId, FieldName
);
SELECT cf.CandidateID, COUNT(*) AS matches
FROM #candidateFields cf
JOIN Table_LKP_AlertMastInfo alerts
ON alerts.DistrictID = cf.DistrictID
AND alerts.FieldName = cf.FieldName
AND alerts.AlertOptionValue = cf.FieldValue
GROUP BY cf.CandidateID
END;
执行
EXEC dbo.findMatches
您当然需要调整类型并可能在此处添加许多其他内容,例如错误处理,但这应该让您走上正确的道路。您将需要该警报的覆盖索引 table,即使有很多记录,它也应该非常快。
未测试,但这应该可以解决问题:
SELECT CandidateId,
( CASE
WHEN AreYouMarried = "Yes" AND Gender = 'Female' THEN 2
WHEN Gender = 'Female' THEN 1
WHEN AreYouMarried = "Yes" THEN 1
ELSE 0 END
) as CandidateValue
FROM
(SELECT * FROM Table_LKP_AlertMastInfo) as Alert
LEFT JOIN
(SELECT * FROM Table_RegistrationInfo) as Registration
ON (Alert.DistrictID = Registration.DistrictID);
这应该会为您提供一个列表,其中 candidateId 与条件计数相匹配
我想按区统计考生的提醒
以下是分区警报查找table
Table_LKP_AlertMastInfo
DistrictID FieldName AlertOptionValue
71 AreYouMarried Yes
71 Gender Female
72 AreYouMarried Yes
上面的 Table_LKP_AlertMastInfo FieldName 应该与 table_RegistrationInfo 字段进行比较以检查 AlertOptionValue 以获得计数。
以下是候选人的详细信息table:
Table_RegistrationInfo
CandidateId DistrictID AreYouMarried Gender
Can001 71 Yes Female
Can002 71 No Female
Can003 72 Yes Man
Can004 72 No Man
我想要如下输出:
Can001 2
Can002 1
Can003 1
以上输出计数的解释:
Can001 have selected AreYouMarried:Yes and Gender:Female then count value 2
Can002 have selected Gender:Female then count value 1
Can003 have selected AreYouMarried:Yes then count value 1
Can004 have not alerts
我设法在不使用动态查询的情况下获得了预期的结果。 不确定这是否是您要查找的内容:
SELECT DISTINCT
c.CandidateId, SUM(a.AreYouMarriedAlert + a.GenderAlter) AS AlterCount
FROM
Table_RegistrationInfo c
OUTER APPLY
(
SELECT
CASE
WHEN a.FieldName = 'AreYouMarried' AND c.AreYouMarried = a.AlertOptionValue THEN 1
ELSE 0
END AS AreYouMarriedAlert,
CASE
WHEN a.FieldName = 'Gender' AND c.Gender = a.AlertOptionValue THEN 1
ELSE 0
END AS GenderAlter
FROM
Table_LKP_AlertMastInfo a
WHERE
a.DistrictID = c.DistrictID
) a
GROUP BY c.CandidateId
HAVING SUM(a.AreYouMarriedAlert + a.GenderAlter) > 0
结果:
这是一种简单的方法:
SELECT subq.*
FROM
(SELECT CandidateId,
(SELECT COUNT(*)
FROM Table_LKP_AlertMastInfo ami
WHERE ami.DistrictID = ri.DistrictID
AND ami.FieldName ='AreYouMarried'
AND ami.AlertOptionValue = ri.AreYouMarried) +
(SELECT COUNT(*)
FROM Table_LKP_AlertMastInfo ami
WHERE ami.DistrictID = ri.DistrictID
AND ami.FieldName ='Gender'
AND ami.AlertOptionValue = ri.Gender) AS [count]
FROM Table_RegistrationInfo ri) subq
WHERE subq.[count] > 0;
我假设有 100 个字段,您有一组警报,这些警报是值的组合。此外,我假设您可以始终以正确的顺序拥有一个 select 列表。所以
select candidateid,
AreyouMarried || '|' || Gender all_responses_in_one_string
from ....
是可能的。所以上面会 return
candidateid all_responses_in_one_string
can001 Yes|Female
can002 No|Female
现在您的警报可以是串联字符串的正则表达式。您的警报基于您匹配的数量。
我不确定这是否可以使用 SQL 完全完成。如果您正在使用一些后端技术,例如 ADO.NET,那么您可以将结果存储在 Datatables 中。遍历列名并进行比较。
动态 SQL 可用于使 Table_LKP_AlertMastInfo 看起来像 Table_RegistrationInfo。 此脚本可用于存储过程,结果可在数据表中检索。
DECLARE @SQL NVARCHAR(MAX)
DECLARE @PivotFieldNameList nvarchar(MAX)
SET @SQL = ''
SET @PivotFieldNameList = ''
SELECT @PivotFieldNameList = @PivotFieldNameList + FieldName + ', '
FROM (SELECT DISTINCT FieldName FROM Table_LKP_AlertMastInfo) S
SET @PivotFieldNameList = SUBSTRING(@PivotFieldNameList, 1, LEN(@PivotFieldNameList) - 1)
--SELECT @PivotFieldNameList
SET @SQL = ' SELECT DistrictId, ' + @PivotFieldNameList + ' FROM
Table_LKP_AlertMastInfo
PIVOT
( MAX(AlertOptionValue)
FOR FieldName IN (' + @PivotFieldNameList + '
) ) AS p '
PRINT @SQL
EXEC(@SQL)
以上查询结果如下
DistrictId AreYouMarried Gender
71 Yes Female
72 Yes NULL
如果你从Table_RegistrationInfo中得到结果到另一个数据表中,那么两者都可以用于比较。
如果您的数据是按原样建模的,即 Table_LKP_AlertMastInfo
中的键值对和 Table_RegistrationInfo
中的列,那么如果没有动态 SQL,这将是不可能的。因此,让我们开始吧。提供您需要的确切结果的存储过程的完整代码在最后,我将在后面解释它的作用。
因为警报被指定为键值对(字段名-字段值),我们首先需要以相同的格式获取候选数据。 UNPIVOT
可以解决这个问题,如果我们可以得到它的字段列表。如果我们只有你在问题中提到的两个字段,那会很容易,比如:
SELECT CandidateId, DistrictID
, FieldName
, FieldValue
FROM Table_RegistrationInfo t
UNPIVOT (FieldValue FOR FieldName IN (AreYouMarried, Gender)) upvt
当然不是这样,所以我们需要动态 select 我们感兴趣的字段列表并提供它。由于您使用的是 2008 R2,STRING_AGG is not yet available, so we'll use the XML trick to aggregate all the fields into a single string 并将其提供给上面的查询。
DECLARE @sql NVARCHAR(MAX)
SELECT @sql = CONCAT('SELECT CandidateId, DistrictID
, FieldName
, FieldValue
FROM Table_RegistrationInfo t
UNPIVOT (FieldValue FOR FieldName IN (',
STUFF((
SELECT DISTINCT ',' + ami.FieldName
FROM Table_LKP_AlertMastInfo ami
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''), ')) upvt')
PRINT @sql
这产生的输出几乎与我编写的查询完全一样。接下来,我们需要将这些数据存储在某个地方。临时 tables 来救援。让我们创建一个并使用此动态 SQL.
插入其中CREATE TABLE #candidateFields
(
CandidateID VARCHAR(50),
DistrictID INT,
FieldName NVARCHAR(200),
FieldValue NVARCHAR(1000)
);
INSERT INTO #candidateFields
EXEC sp_executesql @sql
-- (8 rows affected)
-- We could index this for good measure
CREATE UNIQUE CLUSTERED INDEX uxc#candidateFields on #candidateFields
(
CandidateId, DistrictId, FieldName, FieldValue
);
太好了,有了这个,我们现在拥有相同格式的两个数据集 - 警报和候选数据。这是一个加入以找到两者之间匹配的问题:
SELECT cf.CandidateID, COUNT(*) AS matches
FROM #candidateFields cf
INNER
JOIN Table_LKP_AlertMastInfo alerts
ON alerts.DistrictID = cf.DistrictID
AND alerts.FieldName = cf.FieldName
AND alerts.AlertOptionValue = cf.FieldValue
GROUP BY cf.CandidateID
为示例数据提供所需的输出:
CandidateID matches -------------------------------------------------- ----------- Can001 2 Can002 1 Can003 1 (3 rows affected)
所以我们现在可以将所有这些拼接在一起以形成一个可重用的存储过程:
CREATE PROCEDURE dbo.findMatches
AS
BEGIN
SET NOCOUNT ON;
DECLARE @sql NVARCHAR(MAX)
SELECT @sql = CONCAT('SELECT CandidateId, DistrictID
, FieldName
, FieldValue
FROM Table_RegistrationInfo t
UNPIVOT (FieldValue FOR FieldName IN (',
STUFF((
SELECT DISTINCT ',' + ami.FieldName
FROM Table_LKP_AlertMastInfo ami
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''), ')) upvt')
CREATE TABLE #candidateFields
(
CandidateID VARCHAR(50),
DistrictID INT,
FieldName NVARCHAR(200),
FieldValue NVARCHAR(1000)
);
INSERT INTO #candidateFields
EXEC sp_executesql @sql
CREATE UNIQUE CLUSTERED INDEX uxc#candidateFields on #candidateFields
(
CandidateId, DistrictId, FieldName
);
SELECT cf.CandidateID, COUNT(*) AS matches
FROM #candidateFields cf
JOIN Table_LKP_AlertMastInfo alerts
ON alerts.DistrictID = cf.DistrictID
AND alerts.FieldName = cf.FieldName
AND alerts.AlertOptionValue = cf.FieldValue
GROUP BY cf.CandidateID
END;
执行
EXEC dbo.findMatches
您当然需要调整类型并可能在此处添加许多其他内容,例如错误处理,但这应该让您走上正确的道路。您将需要该警报的覆盖索引 table,即使有很多记录,它也应该非常快。
未测试,但这应该可以解决问题:
SELECT CandidateId,
( CASE
WHEN AreYouMarried = "Yes" AND Gender = 'Female' THEN 2
WHEN Gender = 'Female' THEN 1
WHEN AreYouMarried = "Yes" THEN 1
ELSE 0 END
) as CandidateValue
FROM
(SELECT * FROM Table_LKP_AlertMastInfo) as Alert
LEFT JOIN
(SELECT * FROM Table_RegistrationInfo) as Registration
ON (Alert.DistrictID = Registration.DistrictID);
这应该会为您提供一个列表,其中 candidateId 与条件计数相匹配