2015-07-21 24 views
7

Używam SQL-Server 2008. Muszę połączyć wiersze z tego samego licznika Name i zwiększ gdy:Połączyć wiersze, jeśli wartość jest pusta

  1. 1 lub więcej Id's dla tego samego Name jest blank
  2. NIE łączyć wiersze jeśli Id jest NULL!
  3. NIE scalanie wierszy, jeśli mają taką samą Name, ale różni Ids

Wyjście teraz:

Name Id Cnt 
John 1  1 
Peter 2  2 -- This Peter with the same Id have 2 entries so Cnt = 2 
Peter 3  1 -- This is other Peter with 1 entry so Cnt = 1 
Lisa 4  1 
Lisa NULL 1 
David 5  1 
David   1 -- here Id is blank '' 
Ralph   2 -- Ralph have both rows with blank Id so Cnt = 2 

pożądany wynik:

Name Id Cnt 
John 1  1 
Peter 2  2 
Peter 3  1 
Lisa 4  1 
Lisa NULL 1 -- null still here 
David 5  2 -- merged with blank '' so Cnt = 2 
Ralph   2 -- merged both blanks '' so Cnt = 2 

zapytania SQL :

To przykładowe zapytanie co używam teraz:

SELECT Name, 
     Id, 
     COUNT(Id) AS Cnt 
FROM Employees      
WHERE Condition = 1     
GROUP BY Name, Id 

Co próbowałem:

Dodano kruszywo MAX do Id w SELECT klauzuli i pogrupowane według jedynego Name, ale w tym przypadku scalono wiersze o wartościach NULL i o tych samych nazwach z różnymi Id's, co jest dla mnie złe.

SELECT Name, 
     MAX(Id), -- added aggregate 
     COUNT(Id) AS Cnt 
FROM Employees      
WHERE Condition = 1     
GROUP BY Name -- grouped by Name only 

Masz jakieś pomysły? Jeśli coś nie jest jasne na temat problemu - zapytaj mnie, podam więcej szczegółów.

UPDATE:

DLL

CREATE TABLE Employees 
(
    Name NVARCHAR(40), 
    Id NVARCHAR(40) 
); 

DML

INSERT INTO Employees VALUES 
('John' , '1') 
,('Peter', '2') 
,('Peter', '2') 
,('Peter', '3') 
,('Lisa' , '4') 
,('Lisa' , NULL) 
,('David', '5') 
,('David', '') 
,('Ralph', '') 
,('Ralph', '') 

DEMO:SQL FIDDLE

+0

+1 za dobrze sformułowane pytanie. jednak lepiej jest uwzględnić odpowiednie tabele ddl + dml dla przykładowych danych, aby każdy, kto chce odpowiedzieć, mógł je łatwo odtworzyć. –

+1

Dziwny projekt danych w tabeli ... Czy rozważasz zmianę danych? – Anton

+0

@ZoharOferowany drugi, dostarczę DDL + DML –

Odpowiedz

1

Edit

DECLARE @Data table (Name varchar(10), Id varchar(10)) -- Id must be varchar for blank value 
INSERT @Data VALUES 
('John', '1'), 
('Peter', '2'),('Peter', '2'), 
('Peter', '3'),--('Peter', ''), --For test 
('Lisa', '4'), 
('Lisa', NULL), 
('David', '5'), 
('David', ''), 
('Ralph', ''), ('Ralph', '') 

SELECT 
    Name, 
    Id, 
    COUNT(*) + ISNULL(
     (SELECT COUNT(*) FROM @data WHERE Name = d.Name AND Id = '' AND d.Id <> '') 
    , 0) AS Cnt 
FROM @data d 
WHERE 
    Id IS NULL 
    OR Id <> '' 
    OR NOT EXISTS(SELECT * FROM @data WHERE Name = d.Name AND Id <> '') 
GROUP BY Name, Id 
+0

@ StanislovasKalašnikovas Zobacz edytuj – Eric

1

Możesz użyć oświadczenia CASE wewnątrz swojego SELECT. Pozwala ustawić Id = [pewną wartość] dla pracowników, w których jest pusta.Zapytanie może być coś takiego:

SELECT E.Name, 
      CASE 
        WHEN E.Id = '' 
        THEN 
         (Select Employees.Id from Employees where Employees.Id <> '' and E.Name = Employees.Name) 
        ELSE E.Id 
      END as Idx, 
     COUNT(Id) AS Cnt 
FROM Employees as E      
WHERE Condition = 1     
GROUP BY Name, Idx 
0

Spróbuj tego. stosując CTE i łączy

;with cte as (
SELECT Name, 
     Id, 
     COUNT(*) AS Cnt 
FROM Employees      
WHERE isnull(Id,1)<>''    
GROUP BY Name, Id 
), 

cte2 as (SELECT Name,id, COUNT(*) AS Cnt FROM Employees WHERE Id='' GROUP BY Name,id) 

select cte.Name,cte.Id,(cte.cnt + ISNULL(cte2.Cnt,0)) as cnt 
from cte 
left JOIN cte2 
on cte.Name = cte2.Name 
union all 
select cte2.Name,cte2.Id,cte2.cnt 
from cte2 
left JOIN cte 
on cte.Name = cte2.Name 
where cte.Name is null 
1

wersji z funkcji okienkujących:

SELECT Name,ID, Cnt from 
(select *, sum(1-AmtBlank) over (partition by Name, ID) + sum(case id when 0 then 1 else 0 end) over (partition by Name) Cnt, 
     rank() over (partition by Name order by AmtBlank ) rnk, 
     row_number() over (partition by Name, ID order by AmtBlank) rnr 
    FROM (select * , case id when '' then 1 else 0 end AmtBlank from Employees /*WHERE Condition = 1*/) e 
) c where rnr=1 and rnk = 1 

używa case id when '' then 1 else 0 end AmtBlank utrzymać ilość dla pustych wynosi w rzędzie (co ilość dla nie puste 1 AmtBlank) i 2 funkcje okna, jedna z id dla liczby na nazwę i id (sum(1-AmtBlank) over (partition by Name, ID)) i liczba dla wszystkich spacji w sekcji nazwy (sum(case id when 0 then 1 else 0 end) over (partition by Name)) Do następnego pobierania wierszy używa się tylko pierwszych wierszy grupy, a rank służy tylko do dołącz puste rekordy whe n nie ma rekordów z identyfikatorem.

0

Ta prosta składnia jest kompatybilny ze starszymi wersjami lub innych RDBMSs
- Własna wyjaśnione na komentarze
edycja:

select name, id, count(*) from (
    -- adds "normal" records 
    select name, id from Employees where id is null or id <> '' 
    -- adds one record to each name-notBlankId for each blank id (David, Peter if you add 'Peter','') 
    -- uncomment /*or id is null*/ if you want even null ids to recieve merged blanks 
    union all 
    select e1.name, e1.id 
    from (select distinct name, id from Employees where id <> '' /*or id is null*/) as e1 
    inner join (select name, id from Employees where id = '') as e2 on e1.name = e2.name 
    -- adds records that can't be merged (Ralph) 
    union all 
    select name, id from Employees e1 
    where e1.id = '' 
     and not exists(select * from Employees e2 where e1.name = e2.name and e2.id <> '') 
) as fullrecords 
group by name, id 
0

można spróbować coś takiego.

;WITH NonBlanks AS 
(
    SELECT Name, 
     Id, 
     COUNT(ISNULL(Id, 1)) AS Cnt 
    FROM Employees 
    WHERE ISNULL(Id,0) <> '' 
    GROUP BY Name, Id 
) 
,Blanks AS 
(
    SELECT Name, 
     Id, 
     COUNT(ISNULL(Id, 1)) AS Cnt 
    FROM Employees 
    WHERE ID = '' 
    GROUP BY Name, Id 
) 
SELECT CASE WHEN nb.NAME IS NULL THEN b.NAME ELSE nb.NAME END NAME, 
     CASE WHEN nb.NAME IS NULL THEN b.Id ELSE nb.Id END Id, 
     (ISNULL(nb.Cnt,0) + ISNULL(b.Cnt,0)) Cnt 
FROM NonBlanks nb FULL JOIN Blanks b 
    ON nb.Name = b.Name