определить некоторые характеристики с помощью tsql - PullRequest
1 голос
/ 03 февраля 2011

У меня есть документы, которые могут принадлежать нескольким классам и содержать несколько токенов (слов):

create table Tokens (
        Id INT not null,
       Text NVARCHAR(255) null,
       primary key (Id)
    )

create table DocumentClassTokens (
        Id INT not null,
       DocumentFk INT null,
       ClassFk INT null,
       TokenFk INT null,
       primary key (Id)
    )

Я бы хотел определить эти характеристики (для всех жетонов данного класса):

  • A = количество отдельных документов, которые содержат токен и относятся к классу
  • B = количество отдельных документов, которые содержат токен и не относятся к классу
  • C = количество отдельных документов, которые не содержат токена и относятся к классу
  • D = количество отдельных документов, которые не содержат токена и не относятся к классу

Я использую это в данный момент, но это выглядит неправильно (я почти уверен, что вычисления A и B правильные):

declare @class int;

select @class = id from dbo.Classes where text = 'bla'

;with A as
(
    select
        a.text as token,
        count(distinct DocumentFk) as A
    from dbo.Tokens as a
    inner join dbo.DocumentClassTokens as b on a.id = b.TokenFk and b.ClassFk = @class
    group by a.text
)
,B as
(
    select
        a.text as token,
        count(distinct DocumentFk) as B
    from dbo.Tokens as a
    inner join dbo.DocumentClassTokens as b on a.id = b.TokenFk and b.ClassFk != @class
    group by a.text
)
,C as
(
    select
        a.text as token,
        count(distinct DocumentFk) as C
    from dbo.Tokens as a
    inner join dbo.DocumentClassTokens as b on a.id != b.TokenFk and b.ClassFk = @class
    group by a.text
)
,D as
(
    select
        a.text as token,
        count(distinct DocumentFk) as D
    from dbo.Tokens as a
    inner join dbo.DocumentClassTokens as b on a.id != b.TokenFk and b.ClassFk != @class
    group by a.text
)
select 
    case when A is null then 0 else A end as A,
    case when B is null then 0 else B end as B,
    case when C is null then 0 else C end as C,
    case when D is null then 0 else D end as D,
    t.Text,
    t.id
from dbo.Tokens as t
left outer join A as a on t.text = a.token
left outer join B as b on t.text = b.token
left outer join C as c on t.text = c.token
left outer join D as d on t.text = d.token
order by t.text

Любая обратная связь будет принята с благодарностью. Большое спасибо!

С наилучшими пожеланиями,

Christian

PS:

Некоторые данные испытаний:

use play;

drop table tokens
create table Tokens 
(
   Id INT not null,
   Text NVARCHAR(255) null,
   primary key (Id)
)

insert into Tokens (id, text) values (1,'1')
insert into Tokens (id, text) values (2,'2')

drop table DocumentClassTokens
create table DocumentClassTokens (
        Id INT not null,
       DocumentFk INT null,
       ClassFk INT null,
       TokenFk INT null,
       primary key (Id)
    )

insert into DocumentClassTokens (Id,documentfk,ClassFk,TokenFk) values (1,1,1,1) 
insert into DocumentClassTokens (Id,documentfk,ClassFk,TokenFk) values (2,1,1,2) 
insert into DocumentClassTokens (Id,documentfk,ClassFk,TokenFk) values (3,2,1,1) 
insert into DocumentClassTokens (Id,documentfk,ClassFk,TokenFk) values (4,2,2,1) 
insert into DocumentClassTokens (Id,documentfk,ClassFk,TokenFk) values (5,3,2,1) 
insert into DocumentClassTokens (Id,documentfk,ClassFk,TokenFk) values (6,3,2,3)  

Ответы [ 2 ]

1 голос
/ 05 февраля 2011

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

DECLARE @class int;
SET @class = 1;

SELECT
  TokenFk,
  TokenClassDocs                        AS A,
  TokenNonClassDocs                     AS B,
  TotalClassDocs    - TokenClassDocs    AS C,
  TotalNonClassDocs - TokenNonClassDocs AS D
FROM (
  SELECT
    TokenFk,
    COUNT(DISTINCT CASE ClassFk WHEN @class THEN DocumentFk ELSE NULL END) AS TokenClassDocs,
    COUNT(DISTINCT CASE ClassFk WHEN @class THEN NULL ELSE DocumentFk END) AS TokenNonClassDocs
  FROM DocumentClassTokens dct
  GROUP BY dct.TokenFk
) AS bytoken
  CROSS JOIN (
    SELECT
      COUNT(DISTINCT CASE ClassFk WHEN @class THEN DocumentFk ELSE NULL END) AS TotalClassDocs,
      COUNT(DISTINCT CASE ClassFk WHEN @class THEN NULL ELSE DocumentFk END) AS TotalNonClassDocs
    FROM DocumentClassTokens
  ) AS totals

Пожалуйста, дайте нам знать, если все в порядке.


EDIT

Приведенное выше решение неверно. Вот исправленный, и он, конечно, кажется правильным, только мне он не нравится так сильно, как неправильная версия (что за ирония ...).

DECLARE @class int;
SET @class = 1;

SELECT
  TokenFk,
  TokenClassDocs                        AS A,
  TokenNonClassDocs                     AS B,
  TotalClassDocs    - TokenClassDocs    AS C,
  TotalNonClassDocs - TokenNonClassDocs AS D
FROM (
  SELECT
    TokenFk,
    COUNT(DISTINCT cls.DocumentFk) AS TokenClassDocs,
    COUNT(DISTINCT CASE WHEN cls.DocumentFk IS NULL THEN dct.DocumentFk END) AS TokenNonClassDocs
  FROM DocumentClassTokens dct
    LEFT JOIN (
      SELECT DISTINCT DocumentFk
      FROM DocumentClassTokens
      WHERE ClassFk = @class
    ) cls ON dct.DocumentFk = cls.DocumentFk
  GROUP BY dct.TokenFk
) AS bytoken
  CROSS JOIN (
    SELECT
      COUNT(DISTINCT cls.DocumentFk) AS TotalClassDocs,
      COUNT(DISTINCT CASE WHEN cls.DocumentFk IS NULL THEN dct.DocumentFk END) AS TotalNonClassDocs
    FROM DocumentClassTokens dct
      LEFT JOIN (
        SELECT DISTINCT DocumentFk
        FROM DocumentClassTokens
        WHERE ClassFk = @class
      ) cls ON dct.DocumentFk = cls.DocumentFk
  ) AS totals

Примечание: я думаю, что теперь я вижу, как можно проверить, верны ли цифры: сумма A, B, C, D в каждой строке (то есть для каждого токена) должна быть равна общему количеству документов, что не должно быть ничего удивительного, потому что каждый документ может удовлетворить 1 и только 1 из 4 рассматриваемых случаев. Если сумма строк отличается от общего количества документов, то некоторые цифры в строке, безусловно, неверны.

1 голос
/ 03 февраля 2011

Это швы, чтобы делать то, что вы хотите по вашему описанию. Глядя на твой код, я не уверен.

Редактировать 1 С столбцами вместо строк и @ClassID в качестве фильтра.

declare @ClassID int
set @ClassID = 1

;with cte(DokumentFk, TokenFk, ClassFk) as
(
  select DocumentFk, max(TokenFK), max(ClassFk) 
  from DocumentClassTokens
  where ClassFK = @ClassID
  group by DocumentFK
)
select
(select count(*)
 from cte
 where
   TokenFk is not null and
   ClassFk is not null) as A,
(select count(*)
 from cte
 where
   TokenFk is not null and
   ClassFk is null) as B,
(select count(*)
 from cte
 where
   TokenFk is null and
   ClassFk is not null) as C,
(select count(*)
 from cte
 where
   TokenFk is null and
   ClassFk is null) as D
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...