SQL-запрос для получения количества строк с более чем одной строкой в ​​другой таблице - PullRequest
3 голосов
/ 11 августа 2011

В моем приложении у меня есть набор фильтров, которые можно применять при перечислении ресурсов, который создает запрос путем добавления предложений WHERE и т. Д. Перед выполнением запроса.Это использует SQL Server 2008.

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

Перваятаблица выглядит примерно так (имена и поля таблицы изменены):

CREATE TABLE Resources (
    ResID       varbinary(28),

    ... extra stuff omitted

    type        integer );

Во второй таблице просто есть пары имя / значение и соответствующий идентификатор ресурса

CREATE TABLE ResourceFields (
    ResID       varbinary(28) NOT NULL,

    Name        nvarchar(255) NOT NULL,
    Value       nvarchar(1024) NOT NULL);

Итак, для этого примера, в ResourceFields может быть несколько строк с именем = «ContactName» для одного и того же ResID.

Что я хочу сделать, так это получить количество строк в таблице «Ресурсы»которые имеют более одного «ContactName» в списке «ResourceFields» с «типом», равным некоторому значению.

Я придумал это (не смейтесь - я знаю достаточно SQL, чтобы вызватьпроблемы)

SELECT count(r.ResID) 
    FROM Resources as r 
        INNER JOIN ResourceFields AS rf 
            ON rf.ResID = r.ResID 
                AND rf.name = 'ContactName' 
    WHERE r.type = 1 
    GROUP BY rf.ResID 
    HAVING COUNT(rf.Value) > 1;

но вместо того, чтобы возвращать счетчик количества строк (43 в моем наборе тестов) в «Ресурсах», я получаю все возвращенные значения COUNT (rf.Value) (то есть43 индивидуальных сотрудникаunts).

Что я делаю не так?

Ответы [ 4 ]

8 голосов
/ 11 августа 2011

Просто используйте исходный запрос в качестве производной таблицы (поместите его в подвыбор):

SELECT COUNT(*)
FROM (
    SELECT count(*) AS C
    FROM Resources as r 
        INNER JOIN ResourceFields AS rf 
            ON rf.ResID = r.ResID 
                AND rf.name = 'ContactName' 
    WHERE r.type = 1 
    GROUP BY rf.ResID 
    HAVING COUNT(rf.Value) > 1;
) s
1 голос
/ 11 августа 2011

Необходимо предварительно агрегировать количество атрибутов «ContactName».Возможно, это проще всего сделать с помощью CTE:

WITH Multiple_Contacts (ResID) as (SELECT a.ResID
                                   FROM Resources as a
                                   JOIN ResourceFields as B
                                   ON b.ResID = A.ResID
                                   AND b.name = 'ContactName'
                                   WHERE a.type = 1
                                   GROUP BY a.ResID
                                   HAVING COUNT(a.ResId) > 1)
SELECT COUNT(ResId)
FROM Multiple_Contacts

Некоторые другие соображения, которые следует учитывать -
Возможно, сделать name в ResourceFields фактически внешним ключом для другой таблицы (поэтому меняются всеатрибуты к другому текстовому имени тривиальны).Это также позволит вам поместить информацию об ожидаемом формате данных в value в ссылочную таблицу, надеясь ограничить недопустимые данные (используя маски регулярных выражений и т. Д.) - вы должны быть очень осторожны с такими многодоменными таблицами, как этокак правило, их не следует использовать, но, вероятно, есть некоторые варианты использования).
Кроме того, вы действительно ожидаете хранения 28 байт различных ресурсов?Это довольно большое число ... (помните, что Int обычно составляет 4 байта и хранит около 4 миллиардов различных значений).

1 голос
/ 11 августа 2011

Я не очень знаком с SQL Server, но попробуйте:

SELECT count( r.ResID ) FROM Resources as r where 1 < (select count(rf.value) from ResourceFields AS rf where rf.ResID = r.ResID AND rf.name = 'ContactName') and r.type = 1;
0 голосов
/ 11 августа 2011

Надеюсь, это поможет:

SELECT COUNT(*)
FROM Resources as r
WHERE EXISTS (
    SELECT 1
    FROM ResourceFields rf
    WHERE rf.ResId = r.ResId
    AND rf.name = 'ContactName'
    HAVING COUNT(*) > 1
)
AND r.type = 1

ОБНОВЛЕНИЕ: Группировка удалена из подзапроса, что добавило несущественные строки в число.

...