避免工会获得TOP计数

时间:2018-05-03 16:03:40

标签: sql sql-server

以下是两个表格:

LocationId   Address          City    State    Zip
1            2100, 1st St     Austin  TX       76819
2            2200, 2nd St     Austin  TX       76829
3            2300, 3rd St     Austin  TX       76839
4            2400, 4th St     Austin  TX       76849
5            2500, 5th St     Austin  TX       76859
6            2600, 6th St     Austin  TX       76869


TripId   PassengerId          FromLocationId    ToLocationId
1        746896               1                 2
2        746896               2                 1
3        234456               1                 3
4        234456               3                 1
5        234456               1                 4
6        234456               4                 1
7        234456               1                 6
8        234456               6                 1
9        746896               1                 2
10       746896               2                 1
11       746896               1                 2
12       746896               2                 1

我想要每位乘客前往的前5个位置(无论是从哪里到位置都无关紧要)。我可以使用UNION来获取它,但是想知道是否有更好的方法来做到这一点。

我的解决方案:

select  top 5 *
from 
(select count(l.LocationId) as cnt, l.LocationId, l.Address1, l.Address2, l.City, St.State , l.Zip
from 
Trip t
join LOCATION l on t.FromLocationId = l.LocationId
where t.PassengerId = 746896
group by count(l.LocationId) as cnt, l.LocationId, l.Address1, l.Address2, l.City, St.State , l.Zip
UNION
select count(l.LocationId) as cnt, l.LocationId, l.Address1, l.Address2, l.City, St.State , l.Zip
from 
Trip t
join LOCATION l on t.ToLocationId = l.LocationId
where t.PassengerId = 746896
group by count(l.LocationId) as cnt, l.LocationId, l.Address1, l.Address2, l.City, St.State , l.Zip
) as tbl
order by cnt desc

4 个答案:

答案 0 :(得分:1)

你需要用你需要的列替换SELECT *,但是,这样的事情应该有效:

WITH Visits AS (
    SELECT *,
           COUNT(*) OVER (PARTITION BY t.PassengerID, L.LocationID) AS Visits
    FROM Trip T
         JOIN [Location] L ON T.FromLocationId = L.LocationId),
Rankings AS (
    SELECT *,
           DENSE_RANK() OVER (PARTITION BY V.PassengerID ORDER BY Visits DESC) AS Ranking
    FROM Visits V)
SELECT *
FROM Rankings
WHERE Ranking <= 5;

答案 1 :(得分:1)

您可以在不union all的情况下执行此操作:

select top (5) t.passengerid, v.locationid, count(*)
from trip t cross apply
     (values (fromlocationid), (tolocationid)) v(locationid) join
     location l
     on v.locationid = l.locationid
where t.PassengerId = 746896
group by  t.passengerid, v.locationid
order by count(*) desc;

如果您想要所有乘客的答案,使用row_number()会有类似的想法,但您的查询建议您一次只能为一位客户提供答案。

您也可以在location中添加其他字段。

Here是一个SQL小提琴。

答案 2 :(得分:1)

进一步简化的解决方案

  select  top 3 * from
( 

  Select  distinct count(locationId) as cnt, locationId from trip 
  unpivot
  (
   locationId 
   for direction in (fromLocationId, toLocationId)
  )u 
  where passengerId IN (746896, 234456)
  group by direction,  locationId
)as tbl2  

  order by cnt desc;

组合列的解决方案

对我来说,主要问题是避免联合组合这两列。 UNPIVOT命令可以执行此操作。

select  top 3 * from (
  select count(locationId) cnt, locationId
from
(

  Select valu as locationId, passengerId from trip
  unpivot
  (
   valu 
   for loc in (fromLocationId, toLocationId)
  )u
)united  
where passengerId IN (746896, 234456)
group by locationId
 ) as tbl
order by cnt desc;

http://sqlfiddle.com/#!18/cec8b/136

如果你想按指示得到计数:

select  top 3 * from (
  select count(locationId) cnt, locationId, direction
from
(

  Select valu as locationId, direction, passengerId from trip
  unpivot
  (
   valu 
   for direction in (fromLocationId, toLocationId)
  )u
)united  
where passengerId IN (746896, 234456)
group by locationId, direction
 ) as tbl
order by cnt desc;

http://sqlfiddle.com/#!18/cec8b/139

与您相同的结果(减去一些次要描述)

select  top 3 * from
(

select  distinct * from (
  select count(locationId) cnt, locationId 
from
(

  Select valu as locationId, direction, passengerId from trip
  unpivot
  (
   valu 
   for direction in (fromLocationId, toLocationId)
  )u
)united  
where passengerId IN (746896, 234456)
group by locationId, direction
 ) as tbl
)as tbl2  
order by cnt desc;

答案 3 :(得分:1)

这将为您提供前五名。

SELECT TOP 5 tmp.fromlocationid        AS locationid, 
             Count(tmp.fromlocationid) AS Times 
FROM   (SELECT fromlocationid 
        FROM   trip 
        UNION ALL 
        SELECT tolocationid 
        FROM   trip) tmp 
GROUP  BY tmp.fromlocationid 

方法1:这将为您提供每位乘客的前5位。

WITH cte AS
(   SELECT passengerid, 
        locationid, 
        Count(locationid) AS Times, 
        Row_number() OVER(partition BY passengerid ORDER BY passengerid ASC) AS RowNum 
    FROM   (SELECT tripid, passengerid, fromlocationid AS locationid 
            FROM   trip 
            UNION ALL 
            SELECT tripid, passengerid, tolocationid AS locationid 
            FROM   trip) tmp 
    GROUP  BY passengerid, locationid ) 


SELECT * 
FROM   cte 
WHERE  rownum <= 5 
ORDER  BY passengerid, Times DESC 

方法2:没有联盟运营商的结果相同(每位乘客的前5位)

WITH cte AS
(   SELECT passengerid, 
        locationid, 
        Count(locationid) AS Times, 
        Row_number() OVER(partition BY passengerid ORDER BY passengerid ASC) AS RowNum 
    FROM   trip 
        UNPIVOT ( locationid 
                FOR subject IN (fromlocationid, tolocationid) ) u 
    GROUP  BY passengerid, locationid ) 


SELECT * 
FROM   cte 
WHERE  rownum <= 5 
ORDER  BY passengerid, times DESC 

如果您还想获取位置详细信息,只需加入位置表即可。

SELECT cte.* , location.* 
FROM   cte 
INNER  JOIN location ON location.locationid = cte.locationid
WHERE  rownum <= 5 
ORDER  BY passengerid, times DESC 

参考
- https://stackoverflow.com/a/19056083/6327676