SQL-запрос для получения подсчета из двух таблиц на основе значений и имен полей - PullRequest
5 голосов
/ 22 марта 2019

Я хочу посчитать предупреждения кандидатов на основе района.

Ниже приведена таблица поиска предупреждений по районам

Table_LKP_AlertMastInfo

DistrictID             FieldName              AlertOptionValue  
  71                    AreYouMarried                 Yes
  71                      Gender                      Female
  72                    AreYouMarried                 Yes

Приведенное выше поле Table_LKP_AlertMastInfo должно сравниваться с полями table_RegistrationInfo, чтобыпроверьте AlertOptionValue, чтобы получить счет.

Ниже приведена таблица сведений о кандидате:

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 

Ответы [ 6 ]

6 голосов
/ 01 апреля 2019

Это невозможно без использования динамического SQL, если ваши данные смоделированы как есть, то есть пары ключ-значение в Table_LKP_AlertMastInfo и столбцы в Table_RegistrationInfo. Так что с этим из нашего пути, давайте сделаем это. Полный код хранимой процедуры, предоставляющий точные результаты, которые вам нужны, приведен в конце, я последую за объяснением того, что она делает.

Поскольку предупреждения указываются в виде пар ключ-значение (имя поля - значение поля), нам сначала нужно получить данные кандидата в том же формате. UNPIVOT может исправить это, если мы сможем получить список полей. Если бы у нас было только два поля, которые вы упомянули в вопросе, это было бы довольно просто, что-то вроде:

SELECT CandidateId, DistrictID
     , FieldName
     , FieldValue
  FROM Table_RegistrationInfo t
  UNPIVOT (FieldValue FOR FieldName IN (AreYouMarried, Gender)) upvt

Конечно, это не так, поэтому нам нужно динамически выбрать список полей, которые нас интересуют, и предоставить это. Поскольку вы работаете в 2008 R2, STRING_AGG еще не доступен, поэтому мы будем использовать трюк XML, чтобы объединить все поля в одну строку и предоставить ее для вышеприведенного запроса. .

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

Это дает почти точный результат в виде запроса, который я написал Далее нам нужно где-то хранить эти данные. Временные столы на помощь. Давайте создадим один и вставим в него, используя этот динамический 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

Вам, конечно, нужно настроить типы и, возможно, добавить сюда кучу других вещей, например, обработку ошибок, но это должно привести вас к правильному пути. Вам понадобится индекс покрытия для этой таблицы оповещений, и он должен быть довольно быстрым даже при большом количестве записей.

2 голосов
/ 29 марта 2019

Я полагаю, что с 100 полями у вас есть набор предупреждений, которые представляют собой комбинацию значений. Далее я предполагаю, что вы можете иметь список выбора в правильном порядке все время. Итак

select candidateid,
       AreyouMarried || '|' || Gender all_responses_in_one_string
from ....

возможно. Так что выше вернется

candidateid  all_responses_in_one_string
can001       Yes|Female
can002       No|Female

Так что теперь ваше предупреждение может быть регулярным выражением для объединенной строки. А ваше оповещение основано на том, насколько вы соответствовали.

2 голосов
/ 27 марта 2019

Мне удалось получить ожидаемый результат без использования динамических запросов.Не уверен, что это то, что вы ищете:

 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 

Результаты:

enter image description here

1 голос
/ 29 марта 2019

Я не уверен, что это можно сделать полностью с помощью SQL. Если вы используете некоторую внутреннюю технологию, такую ​​как ADO.NET, вы можете сохранить результаты в Datatables. Переберите имена столбцов и сделайте сравнение.

Динамический SQL может использоваться для того, чтобы Table_LKP_AlertMastInfo был похож на Table_RegistrationInfo. Этот сценарий можно использовать в хранимой процедуре, а результаты можно получить в Datatable.

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 в другой Datatable, то оба могут использоваться для сравнения.

1 голос
/ 28 марта 2019

Вот один простой способ сделать это:

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;

См. Демонстрация SQL Fiddle .

0 голосов
/ 03 апреля 2019

Не проверено, но это должно сработать:

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);

Это должно дать вам список с кандидатом, соответствующим условному количеству

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...