【问题标题】:Complex SQL writing复杂的SQL编写
【发布时间】:2012-06-06 11:40:19
【问题描述】:

我有这张桌子:

table session(
ID number,
SessionID VarChar,
Date,
Filter
)

此表包含搜索信息,如下所示:

ID  SessionID                   Date                filter
4   peqq421gaspts3nuulq5mwcq    24/05/2012 13:48    meagPixel=5
6   peqq421gaspts3nuulq5mwcq    24/05/2012 13:48    brand=Canon
7   peqq421gaspts3nuulq5mwcq    24/05/2012 13:48    brand=Canon&meagPixel=12.1
8   peqq421gaspts3nuulq5mwcq    24/05/2012 13:48    brand=Canon
10  peqq421gaspts3nuulq5mwcq    24/05/2012 13:48    brand=Nikon
12  peqq421gaspts3nuulq5mwcq    24/05/2012 13:48    meagPixel=12.1
13  peqq421gaspts3nuulq5mwcq    24/05/2012 13:48    meagPixel=12.1&opticalZoom=True
14  peqq421gaspts3nuulq5mwcq    24/05/2012 13:49    meagPixel=12.1&opticalZoom=True&brand=Panasonic
16  peqq421gaspts3nuulq5mwcq    24/05/2012 13:49    price=500.00
18  peqq421gaspts3nuulq5mwcq    24/05/2012 13:49    price=499.00
19  peqq421gaspts3nuulq5mwcq    24/05/2012 13:49    price=499.00&brand=Olympus
21  peqq421gaspts3nuulq5mwcq    24/05/2012 13:49    zoomRange=2000
22  peqq421gaspts3nuulq5mwcq    24/05/2012 13:49    zoomRange=2000&brand=Leica
23  peqq421gaspts3nuulq5mwcq    24/05/2012 13:49    zoomRange=2000&brand=Leica&price=1995.00
24  peqq421gaspts3nuulq5mwcq    24/05/2012 13:49    zoomRange=2000&brand=Leica&price=1995.00&opticalZoom=True
25  peqq421gaspts3nuulq5mwcq    24/05/2012 13:49    zoomRange=2000&brand=Leica&price=1995.00&opticalZoom=True&meagPixel=16.2
26  peqq421gaspts3nuulq5mwcq    24/05/2012 13:50    zoomRange=2000&brand=Leica&price=1995.00&opticalZoom=True&meagPixel=16.2&weight=345
27  peqq421gaspts3nuulq5mwcq    24/05/2012 13:58    zoomRange=2000&brand=Leica&price=1995.00&opticalZoom=True&meagPixel=16.2
41  poiq41111spts00000q5aaaa    27/05/2012 13:48    meagPixel=5

我想获得独特的搜索。唯一搜索是:

  • 用户(会话)的最长搜索(过滤)
  • 如果第一个过滤器发生变化 - 需要将其视为新搜索(过滤器)

由于 ASP.NET 不保证 SessionID 是唯一的(SessionID,Date)是唯一的。

我没走多远:

SELECT        MAX(Filter)
FROM            Session
GROUP BY SessionID

顺便说一句,我给出的示例表数据的结果应该返回:

ID  SessionID                   Date                filter              
4   peqq421gaspts3nuulq5mwcq    24/05/2012 13:48    meagPixel=5     
7   peqq421gaspts3nuulq5mwcq    24/05/2012 13:48    brand=Canon&meagPixel=12.1      
10  peqq421gaspts3nuulq5mwcq    24/05/2012 13:48    brand=Nikon     
14  peqq421gaspts3nuulq5mwcq    24/05/2012 13:49    meagPixel=12.1&opticalZoom=True&brand=Panasonic     
16  peqq421gaspts3nuulq5mwcq    24/05/2012 13:49    price=500.00        
19  peqq421gaspts3nuulq5mwcq    24/05/2012 13:49    price=499.00&brand=Olympus      
26  peqq421gaspts3nuulq5mwcq    24/05/2012 13:50    zoomRange=2000&brand=Leica&price=1995.00&opticalZoom=True&meagPixel=16.2&weight=345     
41  poiq41111spts00000q5aaaa    27/05/2012 13:48    meagPixel=5     

感谢您的帮助和指导。

【问题讨论】:

  • 您能否再次检查您的预期输出。 brand=Canonbrand=Canon&meagPixel=12.1 具有相同的第一个过滤器,但它们分别列出。虽然 zoomRange=2000&brand=Leica&price=1995.00&opticalZoom=True&meagPixel=16.2&weight=345 只有一个条目,而在主表中之后你有一个记录 zoomRange=2000&brand=Leica&price=1995.00&opticalZoom=True&meagPixel =16.2
  • 因为不是很清楚所以改了。
  • 非常抱歉我现在只编辑我的帖子 - 我使用 sql server compact 4 而不是 sql server 标准版

标签: sql sql-server-ce sql-server-ce-4


【解决方案1】:

@GarethD - 用于模式和插入查询的 Tx。 我尝试了稍微不同的方法。我不确定这是否适用于所有情况。它适用于 mysql 和 mssql。

          select * 
          from tsession t1 
          where  not exists (
                             select * 
                             from tsession t2 
                             where t2.filter  like concat(t1.filter,'%') 
                             and t1.filter<>t2.filter 
                             and t1.sessionid=t2.sessionid) 
          order by id;

这给出了问题中要求的准确结果。

【讨论】:

  • 您在“和过滤器不为空”的外部缺少。我真的不知道它是否能回答所有场景。你这么认为吗?
  • 这肯定会为您提供最长的过滤器 concat(t1.filter,'%') 将确保这一点。需要进一步测试的场景是您在任何 col 上有一些其他条件(例如任何分组要求。)。关于过滤器不为空,从数据中不清楚过滤器可以为空。
  • 是的,我已经在我的真实桌子上测试过它,那里有空值。这将需要更多的测试,因为它似乎太简单了:)
  • 如果为null,您不希望该行出现在结果中吗?
【解决方案2】:

要获得最长的搜索过滤器,您需要执行以下操作:

select s.*
from (select s.*,
             row_number() over (partition by sessionid order by len desc) as rownum
      from (select s.*, len(filter) as len
            from session s
           ) s
     ) s
where rownum = 1

我正在使用 Windows 功能执行此操作。您可以使用聚合和连接来做同样的事情。

但是,您是说会话不是真正的标识符。会话/过滤器是。以下查询几乎可以满足您的需求:

select s.*
from (select s.*,
             row_number() overo over (partition by sessionid, filter 
                                      order by len desc) as rownum
      from (select s.*, len(filter) as len
            from session s
           ) s
     ) s
where rownum = 1

(唯一的变化是分区子句包含过滤器。)

您可能有重复。如果您想要所有重复项,则可以使用稍微不同的查询。

【讨论】:

    【解决方案3】:

    首先,您的示例数据中似乎存在错误,我认为第 25、26 和 27 行都应该出现在您的最终数据中。 27 当然应该,因为它是会话 ID 和日期组合的唯一条目。

    假设以上是正确的,那么我认为我已经正确地建立了您的逻辑。

    第 1 步是为每个过滤器定义第一个搜索词,以及它在会话中出现的顺序:

    ;WITH CTE AS
    (   SELECT  *, 
                SUBSTRING(Filter, 1, CASE WHEN CHARINDEX('&', Filter) = 0 THEN LEN(Filter) ELSE CHARINDEX('&', Filter) - 1 END) [FirstTerm],
        FROM    Session
    )
    

    下一步是确定每次搜索是新搜索还是先前搜索的延续。这是通过获取会话中的上一个搜索词(为什么在最后一个 CTE 中定义 SessionOrder)并确定第一个搜索词是否相同来完成的。

    , CTE2 AS
    (   SELECT  T1.*, 
                CASE WHEN T1.SessionOrder = 1 OR T2.SessionOrder IS NOT NULL THEN 1 ELSE 0 END [NewSearch]
        FROM    CTE T1
                LEFT JOIN CTE T2
                    ON  T1.SessionID = T2.SessionID
                    AND T1.Date = T2.Date
                    AND T1.FirstTerm != T2.FirstTerm
                    AND T1.SessionOrder = T2.SessionOrder + 1
    )
    

    接下来,每个新搜索都需要在会话中拥有自己的排名,以便对目的进行分组。然后您定义了规则(SessionID、日期和第一个搜索词的唯一组合),然后您可以根据过滤器的长度在唯一组合中对每个项目进行排序:

    , CTE3 AS
    (   SELECT  *,
                ROW_NUMBER() OVER(PARTITION BY SessionID, Date, ISNULL(SearchNumber, 0) ORDER BY LEN(Filter) DESC) [SearchOrder]
        FROM    CTE2 T1
                OUTER APPLY
                (   SELECT  SUM(NewSearch) [SearchNumber]
                    FROM    CTE2 T2
                    WHERE   T1.SessionOrder >= T2.SessionOrder
                    AND     T1.SessionID = T2.SessionID
                    AND     T1.Date = T2.Date
                ) c
    )
    

    最后,您需要做的就是将结果限制为 SessionID、Date 和第一个过滤词的每个组合的最长搜索词:

    SELECT  ID, SessionID, Date, Filter
    FROM    CTE3
    WHERE   SearchOrder = 1
    ORDER BY ID
    

    通常我会将所有这些都放在 SQLFiddle 上,而不是在这里发布一个完整的工作示例,但它今天似乎不起作用。所以这是我用来测试你的数据的完整 SQL:

    CREATE TABLE #Session (ID INT, SessionID VARCHAR(50), Date DATETIME, Filter VARCHAR(200))
    INSERT INTO #Session VALUES
        (2, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:48', 'brand=Canon'),
        (4, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:48', 'meagPixel=5'),
        (6, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:48', 'brand=Canon'),
        (7, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:48', 'brand=Canon&meagPixel=12.1'),
        (8, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:48', 'brand=Canon'),
        (10, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:48', 'brand=Nikon'),
        (12, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:48', 'meagPixel=12.1'),
        (13, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:48', 'meagPixel=12.1&opticalZoom=True'),
        (14, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:49', 'meagPixel=12.1&opticalZoom=True&brand=Panasonic'),
        (16, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:49', 'price=500.00'),
        (18, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:49', 'price=499.00'),
        (19, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:49', 'price=499.00&brand=Olympus'),
        (21, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:49', 'zoomRange=2000'),
        (22, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:49', 'zoomRange=2000&brand=Leica'),
        (23, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:49', 'zoomRange=2000&brand=Leica&price=1995.00'),
        (24, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:49', 'zoomRange=2000&brand=Leica&price=1995.00&opticalZoom=True'),
        (25, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:49', 'zoomRange=2000&brand=Leica&price=1995.00&opticalZoom=True&meagPixel=16.2'),
        (26, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:50', 'zoomRange=2000&brand=Leica&price=1995.00&opticalZoom=True&meagPixel=16.2&weight=345'),
        (27, 'peqq421gaspts3nuulq5mwcq', '24/05/2012 13:58', 'zoomRange=2000&brand=Leica&price=1995.00&opticalZoom=True&meagPixel=16.2'),
        (41, 'poiq41111spts00000q5aaaa', '27/05/2012 13:48', 'meagPixel=5')
    
    ;WITH CTE AS
    (   SELECT  *, 
                SUBSTRING(Filter, 1, CASE WHEN CHARINDEX('&', Filter) = 0 THEN LEN(Filter) ELSE CHARINDEX('&', Filter) - 1 END) [FirstTerm],
        FROM    #Session
    ), CTE2 AS
    (   SELECT  T1.*, 
                CASE WHEN T1.SessionOrder = 1 OR T2.SessionOrder IS NOT NULL THEN 1 ELSE 0 END [NewSearch]
        FROM    CTE T1
                LEFT JOIN CTE T2
                    ON  T1.SessionID = T2.SessionID
                    AND T1.Date = T2.Date
                    AND T1.FirstTerm != T2.FirstTerm
                    AND T1.SessionOrder = T2.SessionOrder + 1
    ), CTE3 AS
    (   SELECT  *,
                ROW_NUMBER() OVER(PARTITION BY SessionID, Date, ISNULL(SearchNumber, 0) ORDER BY LEN(Filter) DESC) [SearchOrder]
        FROM    CTE2 T1
                OUTER APPLY
                (   SELECT  SUM(NewSearch) [SearchNumber]
                    FROM    CTE2 T2
                    WHERE   T1.SessionOrder >= T2.SessionOrder
                    AND     T1.SessionID = T2.SessionID
                    AND     T1.Date = T2.Date
                ) c
    )
    SELECT  ID, SessionID, Date, Filter
    FROM    CTE3
    WHERE   SearchOrder = 1
    ORDER BY ID
    
    DROP TABLE #Session
    

    附录

    好的,根据您的结果集,您实际上并不想按日期列分组,您只需将行按按第一个搜索词和 sessionID 分组的长度顺序排列。

    此查询产生的结果与您的示例数据相同。我在 2008 R1 中对此进行了测试,但没有理由认为它在 SQL-Server CE 中不起作用。

    ;WITH CTE AS
    (   SELECT  *,
                ROW_NUMBER() OVER(PARTITION BY SessionID, SUBSTRING(Filter, 1, CASE WHEN CHARINDEX('&', Filter) = 0 THEN LEN(Filter) ELSE CHARINDEX('&', Filter) - 1 END) ORDER BY LEN(Filter) DESC) [RowNumber]
        FROM    Session
    )
    SELECT  *
    FROM    CTE
    WHERE   RowNumber = 1
    ORDER BY ID
    

    最终解决方案的SQL Fiddle

    【讨论】:

    • 我在 25,26,27 行中没有错误。 26 是此过滤器的最长搜索 27 是用户所做的后退一步。
    • 是的,但是 27 和 26 的时间不同;因此根据您的标准,这是一个新会话?
    • @JakeFeasel 我不确定是什么导致了错误。我不断收到消息Unknown Error Occurred: Premature end of file.:。最近刚刚使用 MS SQL-Server 2008 R2 运行 CREATE TABLE T (ID INT);
    • @GarethD 谢谢,很抱歉。我的应用服务器刚刚将其主要配置文件转储给了我。只需重建它,一切就会恢复正常。不知道那里发生了什么!
    • @JakeFeasel 不用担心。因为可以免费使用这样一个很棒的网站;我们可以原谅偶尔的服务中断!
    猜你喜欢
    • 1970-01-01
    • 2014-05-27
    • 1970-01-01
    • 1970-01-01
    • 2021-11-10
    • 1970-01-01
    • 2010-12-08
    • 1970-01-01
    相关资源
    最近更新 更多