T SQL Группировка и выбор количества строк из подзапроса - PullRequest
2 голосов
/ 17 июня 2020

У меня есть следующая таблица

AccountID   Name
1           Foo Bar
2           Jon Dow

AccountID   AddressLine         City
1           123 Test RD         New York
1           456 Test RD         New York
2           Lombard Street      San Francisco
2           Lombard Street      San Francisco

Для данного AccountID я хочу выбрать AddressLine и City.
Если у Account такие же AddressLine, тогда он должен выбрать это значение AddressLine, иначе вернет 'Multiple' .
Если в учетной записи такой же City, тогда следует выбрать это значение City, иначе вернется «Multiple».

Так, например, для идентификатора учетной записи 1 запрос должен вернуть

AccountID  AddressLine City
1          Multiple    New York

Вот SQLFiddle

Ниже мой запрос (не работает) . Я думаю, что проблема заключается в группировке и выборе количества из подзапроса

SELECT
    A.AccountID,
    CASE
        WHEN T1.CNT = 1
            THEN T1.AddressLine
        ELSE 'Multiple'
    END AS 'Address Line',
    CASE WHEN T2.CNT = 1
        THEN T2.City
    ELSE 'Multiple'
    END AS 'City'   
FROM Accounts A
INNER JOIN
(
    SELECT 
        ad.AccountID,
        COUNT(DISTINCT(ad.AccountID)) AS CNT,
        ad.AddressLine
    FROM Addresses ad   
    GROUP BY ad.AccountID, ad.AddressLine
) T1 ON T1.AccountID = A.AccountID
INNER JOIN
(
    SELECT 
        ad.AccountID,
        COUNT(DISTINCT(ad.AccountID)) AS CNT,
        ad.City
    FROM Addresses ad   
    GROUP BY ad.AccountID, ad.City
) T2
ON T2.AccountID = A.AccountID
WHERE 
    a.AccountID = 1

Ответы [ 4 ]

0 голосов
/ 17 июня 2020

Можно также добавить мою в смесь. Не лучше других, но все равно ...

SELECT
    A.AccountID,
    T1.AddressLine,
    T2.City
FROM Accounts A
INNER JOIN
(
    SELECT 
        ad.AccountID,
        CASE WHEN COUNT(DISTINCT(ad.AddressLine)) > 1 THEN 'Multiple' ELSE MIN(ad.AddressLine) END AS AddressLine
    FROM Addresses ad   
    GROUP BY ad.AccountID, ad.city
) T1 ON T1.AccountID = A.AccountID
INNER JOIN
(
    SELECT 
        ad.AccountID,
        CASE WHEN COUNT(DISTINCT(ad.City)) > 1 THEN 'Multiple' ELSE MIN(ad.City) END AS City
    FROM Addresses ad   
    GROUP BY ad.AccountID
) T2
ON T2.AccountID = A.AccountID
WHERE 
    a.AccountID = 1
0 голосов
/ 17 июня 2020

Вот еще одно решение с использованием CROSS APPLY

SELECT
    AccountID
,   CASE 
        WHEN x.countOfDistinctAddressLine = 1 AND x.countOfDistinctCity = 1
        THEN x.firstAddressLine
        ELSE 'Multiple'
        END AS AddressLine
,   CASE 
        WHEN x.countOfDistinctAddressLine = 1 AND x.countOfDistinctCity = 1
        THEN x.firstCity
        ELSE 'Multiple'
        END AS City
FROM
    Addresses AS source
CROSS APPLY
    (
        SELECT
            COUNT(DISTINCT AddressLine) AS countOfDistinctAddressLine
        ,   COUNT(DISTINCT City) AS countOfDistinctCity
        ,   MIN(AddressLine) AS firstAddressLine
        ,   MIN(City) AS firstCity
        FROM
            Addresses
        WHERE
            AccountID = source.AccountID
    ) x
GROUP BY
    AccountID
,   x.countOfDistinctAddressLine
,   x.countOfDistinctCity
,   x.firstAddressLine
,   x.firstCity;
0 голосов
/ 17 июня 2020

Вот простой пример, который вы можете запустить в SSMS:

DECLARE @Accounts TABLE ( AccountID INT, Name VARCHAR(50) );
INSERT INTO @Accounts ( AccountID, Name ) 
    VALUES ( 1, 'Foo Bar' ), ( 2, 'Jon Dow' );

DECLARE @Addresses TABLE ( AccountID INT, AddressLine VARCHAR(50), City VARCHAR(50) );
INSERT INTO @Addresses ( AccountID, AddressLine, City )
    VALUES ( 1, '123 Test Rd', 'New York' ), ( 1, '456 Test Rd', 'New York' ), ( 2, 'Lombard Street', 'San Francisco' ), ( 2, 'Lombard Street', 'San Francisco' );

SELECT
    Accounts.AccountID,
    AddressRecords.AddressLine,
    Addresses.City
FROM @Accounts AS Accounts
INNER JOIN @Addresses AS Addresses
    ON Accounts.AccountID = Addresses.AccountID
OUTER APPLY (

    SELECT CASE
        WHEN ( SELECT COUNT ( DISTINCT ( x.AddressLine ) ) FROM @Addresses AS x WHERE x.AccountID = Accounts.AccountID ) > 1 THEN 'Multiple'
        ELSE ( SELECT DISTINCT AddressLine FROM @Addresses AS x WHERE x.AccountID = Accounts.AccountID )
    END AS AddressLine

) AS AddressRecords
WHERE
    Accounts.AccountID = 1
GROUP BY
    Accounts.AccountID, AddressRecords.AddressLine, Addresses.City;

AccountID 1 RETURNS

+-----------+-------------+----------+
| AccountID | AddressLine |   City   |
+-----------+-------------+----------+
|         1 | Multiple    | New York |
+-----------+-------------+----------+

AccountID 2 RETURNS

+-----------+----------------+---------------+
| AccountID |  AddressLine   |     City      |
+-----------+----------------+---------------+
|         2 | Lombard Street | San Francisco |
+-----------+----------------+---------------+

Конечно , это не учитывает возможность нескольких значений города.

0 голосов
/ 17 июня 2020

Я думаю, это должно сработать. Я использовал функцию string_agg, чтобы свернуть адреса в группе, поскольку вы хотите показать значение только в том случае, если в любом случае есть одно значение. Я отказался от предиката where, чтобы показать только одну учетную запись, но добавить его обратно - тривиально.

;with dist
as
(
  SELECT DISTINCT
  AccountID
  ,AddressLine
  ,City
  FROM addresses
)
,agg
as
(
SELECT 
  AccountID
  ,COUNT(*) as AddressCount
  ,STRING_AGG(AddressLine, ',') as Address
  ,STRING_AGG(City, ',') as City
FROM dist 
GROUP BY AccountID
)

select 
AccountID
,CASE AddressCount 
  WHEN 0 THEN 'N/A'
  WHEN 1 THEN Address
  ELSE 'Multiple' END as Address
,CASE AddressCount 
  WHEN 0 THEN 'N/A'
  WHEN 1 THEN City
  ELSE 'Multiple' END as City
from agg
...