在sql中匹配列表

时间:2014-04-23 13:33:45

标签: sql sql-server sql-server-2012

我正在构建一个包含顾问档案的系统。在该系统中,每位顾问都可以选择他或她拥有的认证。现在,当顾问拥有一定数量的认证时,我正在扩展系统以包含标题。有趣的表是:

Person(stores ID, Firstname etc. of a person)

Certification(stores ID, Name of a certification)

PersonCertification(stores PersonID and CertificationID as a linking table (name?) )

Title(stores the title: ID, Shortname and Fullname)

TitleCertifications(stores which certifications is needed for a title)

举个例子,假设我们有一个名为MCSA的ID为1的标题。此标题所需的认证是ID为1和2的认证.TitleCertifications-table可能如下所示:

TitleID | CertificationID
1       | 1
1       | 2

如果顾问同时拥有身份证1和身份证2的证明,他或她将被授予身份证1的MCSA头衔。如果顾问没有任何证明,则他不会获得此头衔。

我遇到的问题是我不知道如何检查用户是否具有TitleCertifications表中定义的所有必需认证。我已经开始这样做了,但是如果这个人拥有所需的所有证明,它就没有任何检查。

SELECT t.Fullname
FROM Title t
JOIN TitleCertifications tc ON t.ID = tc.TitleID
JOIN PersonCertification pc ON tc.CertificationID = pc.CertificationID

上面的查询结果将为每一行产生一个与认证匹配的titlename,例如,如果用户拥有获得标题的三个必需认证中的两个,它将产生两行。

是否有人知道如何编写符合所需认证的查询,并且只有在用户拥有所有标题所需的认证时才给出答案?

我使用的是使用T-SQL的SQL Server 2012(如果重要的话,在Azure中)。

很抱歉写了一些模糊的,我不确定所有的英文术语。

5 个答案:

答案 0 :(得分:2)

尝试这种方法:

;with pc as (
    select p.personid, tc.titleid, count(*) as cnt_pc
    from person p
    inner join personcertification pc on p.personid = pc.personid
    inner join titlecertifications tc on pc.certificationid = tc.certificationid
    group by p.personid, tc.titleid
),
tc as (
    select t.titleid, count(*) as cnt_tc
    from title t
    inner join titlecertifications tc on t.titleid = tc.titleid
    group by t.titleid
)
select p.firstname, t.shortname
from pc
inner join tc on pc.titleid = tc.titleid
inner join person p on pc.personid = p.personid
inner join title t on pc.titleid = t.titleid
where cnt_pc = cnt_tc

一般的想法是选择需要特定标题的证书数量,以及该标题所拥有的证书数量 - 如果匹配的话,我们认为其中也有一个标题。计数可以通过多种方式完成。

答案 1 :(得分:1)

有很多方法可以做到这一点。这是一个:

select p.ID, p.Name, tcount.ID
from Person p
inner join 
(
    -- Get the count of certs for each title for each person
    select pc.PersonID, t.ID TitleId, count(*) CertCount
    from PersonCertification pc
    inner join Certification c on c.ID = pc.CertificationID
    inner join TitleCertification tc on tc.CertificationId = c.ID
    inner join Title t on t.ID = tc.TitleID
    group by pc.PersonID, t.ID
) cntByTitle on cntByTitle.PerdonID = p.ID
left outer join
(
    select t.TitleID, count(*) CertCount
    from Title t
    inner join TitleCertification tc on tc.TitleId = t.ID
    group by t.ID
) tcount on tcount.TitleID = cntByTitle.TitleID and tcount.PersonID = p.ID
            and tcount.CertCount = cntByTitle.CertCount

注意:cntByTitle子查询可能需要是左外部而不是内部联接:如果一个人必须使用Certs,那么我认为除非你将它设置为左外部,否则它们不会被返回。我还假设所有索引都已到位。如果Person没有标题,则查询将返回Person.ID并返回null。

您可以将其包装为View,然后在更直接的查询中使用View。

答案 2 :(得分:0)

如果您使用的是SQL Server 2012,则可以使用IIF功能。

SELECT IIF(TitleID = '1' AND CertificationID= '1', 1, 0) AS fullName, *
FROM   Title t

...?我想:/

编辑:新的CASE选项

SELECT' name' 案件 当TitleId =' 1' AND CertificateId =' 1'那么MCSA = 1 ELSE MCSA = 0 结束 FROM' table'

答案 3 :(得分:0)

COUNT(columnName)不会计算空值。

以下查询使用左连接来获取此人没有的证书的空值

SELECT MAX(Title.FullName)
FROM Title
     INNER JOIN TitleCertifications
         ON TitleCertifications.TitleID = TitleCertifications.TitleId
     LEFT JOIN PersonCertifications
         ON PersonCertifications.CertificationId = TitleCertifications.CertificationId
WHERE PersonCertifications.PersonId = @Person
      AND TitleCertifications.TitleId = @Title
HAVING COUNT(TitleCertifications.CertificationId) = COUNT(PersonCertifications.CertificationId)

答案 4 :(得分:0)

这应该给你一些方向sql fiddle link

    DECLARE @Person TABLE
(
    Id INT,
    FirstName VARCHAR (20)
)   

DECLARE @Certification TABLE
(
    Id INT,
    CertificateName VARCHAR (20)
)

DECLARE @Title TABLE
(
    TitleId INT,
    Name VARCHAR (20)
)

DECLARE @TitleCertifications TABLE
(
    TitleId INT,
    CertificateId INT
)

DECLARE @PersonCertification TABLE
(
    PersonId INT,
    CertificateId INT
)

INSERT INTO @person VALUES (1, 'tyy'),(2,'Jon'), (3, 'James')

INSERT INTO @Certification VALUES (1, 'MSCA PreReq 1'),(2,'MSCA PreReq 2'),(3,'MSCC PreReq 1')

INSERT INTO @Title VALUES (1, 'MSCA'),(2,'MSCC')

INSERT INTO @TitleCertifications VALUES (1, 1),(1,2), (2,3)

INSERT INTO @PersonCertification VALUES (1, 1),(1,2), (2,1), (3,3)

SELECT * INTO #FlattenedTitleCerts
FROM 
(SELECT g.TitleId, g.Name, STUFF( (SELECT ',' + CAST(CertificateId AS VARCHAR(10)) 
                              FROM @TitleCertifications v
                              WHERE v.TitleId = g.TitleId 
                              ORDER BY CertificateId
                              FOR XML PATH('')),1, 1, '') Combination
FROM @Title g
) t

SELECT * INTO #FlattenedPersonCerts
FROM 
(
    SELECT g.Id, g.FirstName, STUFF( (SELECT ',' + CAST(CertificateId AS varchar(10)) 
                                  FROM @PersonCertification v
                                  WHERE v.PersonId = g.id 
                                  ORDER BY CertificateId
                                  FOR XML PATH('')),1, 1, '') Combination
    FROM @Person g
) t


--SELECT * FROM #FlattenedTitleCerts
--SELECT * FROM #FlattenedPersonCerts

--to get title
SELECT fpc.FirstName Name, 
       ftc.Name Title
FROM #FlattenedPersonCerts fpc
LEFT JOIN #FlattenedTitleCerts ftc ON ftc.Combination = fpc.Combination

DROP TABLE #FlattenedTitleCerts
DROP TABLE #FlattenedPersonCerts