2010-02-24 15 views
1

Je souhaite extraire des données de trois tables dans une base de données SQL Server 2005. Bien que cela puisse sûrement être fait dans le code, il semble que cela pourrait être fait raisonnablement bien en SQL (points bonus pour LINQ!).Nombre d'entrées sur trois tables basées sur un mois dans SQL ou LINQ

Fondamentalement, je voudrais savoir pour chaque mois combien d'appels et de réunions chaque employé a eu avec chacun de nos clients. Quelque chose comme ceci:

Employee GUID Customer GUID Jan calls Jan mtgs Feb calls  Feb mtgs... 
[a guid]  [another guid] 5   0   7    3 

Les données sont réparties entre trois tables. Par souci de simplicité, nous allons montrer que les colonnes correspondantes:

Communications Tableau

[CommunicationId] (PK, uniqueidentifier) 
[Type]    (nvarchar(1)) ('C' for call, 'M' for meeting, etc.) 
[Date]    (datetime) 

Tableau Personne-Communication

[PersonId]   (PK, FK, uniqueidentifier) (Can contain GUIDs for employees or clients, see Person Table below) 
[CommunicationId] (PK, FK, uniqueidentifier) 

Personne Tableau

[PersonId]   (PK, uniqueidentifier) 
[Type]    (nvarchar(1)) ('E' for employee, 'C' for customer) 

Ainsi, questions:

  1. cela peut-il être fait dans SQL sans le code ou horrible de gros problèmes de performance?
  2. Si oui, comment? Je me contenterais même d'une bonne stratégie de haut niveau. Je suppose que pivots jouera un grand rôle ici (en particulier l '"Exemple PIVOT complexe"). DATEPART(MONTH, Date) semble être une bonne méthode pour diviser les communications par mois le long des lignes de:
SELECT DATEPART(MONTH, Date), COUNT(*) 
FROM [CommunicationTable] 
WHERE DATEPART(YEAR, Date) = '2009' 
GROUP BY DATEPART(MONTH, Date) 
ORDER BY DATEPART(MONTH, Date) 

... ce qui me fait le nombre de communications chaque mois en 2009:

1 2871 
2 2639 
3 3654 
4 2751 
5 1773 
6 2575 
7 2906 
8 2398 
9 2621 
10 2638 
11 1705 
12 2290 
+0

@Hogan: le type de données 'uniqueidentifier' n'implique pas la contrainte' UNIQUE'. – Quassnoi

+0

@Hogan: 'uniqueidentifier' est le terme SQL Server pour un GUID. Plusieurs personnes peuvent être associées à plusieurs communications (mais une seule fois, par exemple, être associé à un appel téléphonique trois fois est absurde et interdit). –

+0

Yikes! Je le reprends ... peu importe. – Hogan

Répondre

1

non PIVOT, en utilisant la syntaxe CASE:

WITH summary AS (
     SELECT emp.personid AS emp_guid, 
      cust.personid AS cust_guid, 
      DATEPART(MONTH, ct.date) AS mon, 
      ct.type, 
      COUNT(*) AS num_count 
     FROM COMMUNICATIONTABLE ct 
    LEFT JOIN PERSON_COMMUNICATION pc ON pc.communicationid = ct.communicationid 
     JOIN PERSON emp ON emp.personid = pc.personid 
         AND emp.type = 'E' 
     JOIN PERSON cust ON cust.personid = p.personid 
         AND cust.type = 'C' 
     WHERE ct.date BETWEEN '2009-01-01' AND '2009-12-31' 
    GROUP BY emp.personid, cust.personid, DATEPART(MONTH, ct.ate), ct.type) 
SELECT s.emp_guid, 
     s.cust_guid, 
     MAX(CASE WHEN s.mon = 1 AND s.type = 'C' THEN s.num_count ELSE 0 END) AS "Jan calls", 
     MAX(CASE WHEN s.mon = 1 AND s.type = 'M' THEN s.num_count ELSE 0 END) AS "Jan mtgs", 
     ... --Copy/Paste two lines, update the month check... and the col alias 
    FROM summary s 
GROUP BY s.emp_guid, s.cust_guid 

Utilisez WHERE ct.date BETWEEN '2009-01-01' AND '2009-12-31' car WHERE DATEPART(YEAR, Date) = '2009' ne peut pas utiliser un index s'il en existe un dans la colonne date.

+0

@OMG Poneys: Voir ma réponse - ne sera-t-il pas totaux basés sur Person et client, ou ai-je gâché quelque chose? – Hogan

+0

@OMG Poneys: Ouais dans la table des communications, mais pas dans la table PersonCommunication - je suppose que je suis perdu sur ce que vous voulez dire par somme. – Hogan

+0

@OMG Poneys: Vous avez besoin d'un groupe de plus sur la sélection externe ('group by emp_guid, cust_guid') et un' sum() 'pour chaque cas ou vous obtiendrez plusieurs lignes quand vous en voulez une, vous en aurez une ligne pour total d'appel et une ligne pour total MTT pour chaque mois de la plage. – Hogan

0

Cela devrait vous aider à démarrer, je l'ai fait un mois pour un an vous, vous pouvez également ajouter des restrictions de plage de dates:

SELECT PE.PersonID as EmployeeID,PC2.PersonID as CustomerID, 
SUM(CASE WHEN DATEPART(MONTH, C.[Date]) = 1 
      AND DATEPART(YEAR,C.[Date]) = 2009 
      AND C.[type] = 'C' THEN 1 ELSE 0 END) AS [Jan 2009 Calls] 
FROM PersonTable PE 
JOIN PersonCommunicationTable PC ON PE.PersonID = PC.PersonID 
JOIN CommunicationsTable C ON PC.CommunicationID = C.CommunicationID 
JOIN PersonCommunicationTable PC2 ON PC.CommunicationID = PC2.CommunicationID AND NOT PC2.PersonID = PC.PersonID 
WHERE PE.Type = 'E' 
0

est ici une solution raisonnablement équivalente à l'aide de pivot.

Declare @Comm TABLE 
(
    [CommunicationId] uniqueidentifier PRIMARY KEY DEFAULT NEWID(), 
    [Type] nvarchar(1), -- ('C' for call, 'M' for meeting, etc.) 
    [Date] datetime 
) 

Declare @Person TABLE 
(
    [PersonId] uniqueidentifier PRIMARY KEY DEFAULT NEWID(), 
    [Type]  Nvarchar(1) -- ('E' for employee, 'C' for customer) 
) 
Declare @PersonComm TABLE 
(
    [PersonId] uniqueidentifier, -- (Can contain GUIDs for employees or clients, see Person Table below) 
    [CommunicationId] uniqueidentifier 
) 

INSERT INTO @Person(Type) 
Select 'C' UNION ALL Select 'E' UNION ALL Select 'C' UNION ALL Select 'E' 

INSERT INTO @Comm([Type],[Date]) 
Select 'C', '01/04/2010' UNION ALL Select 'C', '01/04/2010' 
UNION ALL Select 'C', '04/04/2010' UNION ALL Select 'C', '05/01/2010' 
UNION ALL Select 'C', '08/04/2009' UNION ALL Select 'C', '09/01/2009' 
UNION ALL Select 'M', '01/04/2010' UNION ALL Select 'M', '03/20/2010' 
UNION ALL Select 'M', '04/04/2010' UNION ALL Select 'M', '06/01/2010' 
UNION ALL Select 'M', '04/10/2009' UNION ALL Select 'M', '04/10/2009' 

INSERT INTO @PersonComm 
Select E.PersonID , Comm.[CommunicationId] 
FROM @Person E 
     ,@Comm Comm 
Where E.[Type] = 'E' 

INSERT INTO @PersonComm 
Select E.PersonID , Comm.[CommunicationId] 
FROM @Person E 
     ,@Comm Comm 
Where E.[Type] = 'C' 

Select EmployeeID, 
     ClientID, 
     Year, 
     [JanuaryC] AS [Jan Calls], 
     [JanuaryM] AS [Jan Meetings], 
     [FebruaryC], 
     [FebruaryM], 
     [MarchC], 
     [MarchM], 
     [AprilC], 
     [AprilM], 
     [MayC], 
     [MayM], 
     [JuneC], 
     [JuneM], 
     [JulyC], 
     [JulyM], 
     [AugustC], 
     [AugustM], 
     [SeptemberC] , 
     [SeptemberM], 
     [OctoberC] , 
     [OctoberM], 
     [NovemberC], 
     [NovemberM], 
     [DecemberC], 
     [DecemberM] 

FROM 
(
Select P.PersonId EmployeeID, Client.PersonId ClientID, YEAR(C.Date) Year, DateName(m,C.Date) Month, COUNT(*) Amount, C.Type CommType, 
     DateName(m,C.Date) + C.Type PivotColumn -- JanuaryC 
FROM @Comm C 
     INNER JOIN @PersonComm PC 
      ON PC.CommunicationId = C.CommunicationId 
     INNER JOIN @Person P 
      ON P.PersonId = PC.PersonId 
     INNER JOIN @PersonComm PCC 
      ON PCC.CommunicationId = PC.CommunicationId 
     INNER JOIN @Person Client 
      ON Client.PersonId = PCC.PersonId AND Client.Type = 'C' 
Where P.Type = 'E'  
Group By P.PersonId, CLient.PersonId, YEAR(C.Date), DateName(m,C.Date), C.Type 
) SourceTable 
PIVOT (
MAX(Amount) 
FOR PivotColumn IN 
    ([JanuaryC], [JanuaryM],[FebruaryC], [FebruaryM],[MarchC], [MarchM], [AprilC], [AprilM], [MayC], [MayM], [JuneC], [JuneM], [JulyC], [JulyM], 
    [AugustC], [AugustM],[SeptemberC] , [SeptemberM],[OctoberC] ,[OctoberM],[NovemberC], [NovemberM], [DecemberC], [DecemberM] 

) 
)As PivotTable