当前位置: 首页 > 知识库问答 >
问题:

移动中值,模式在T-SQL

凌钊
2023-03-14

我使用的是SQL Server 2012,我知道计算移动平均值非常简单。但我需要的是得到一个定义的窗口框架的模式和中位数,比如(在当前行之前有一个2的窗口;月份唯一):

MONTH | CODE | MEDIAN | MODE
   1      0        0      0
   2      3        1.5    0
   3      2        2      0
   4      2        2      2
   5      2        2      2
   6      5        2      2
   7      3        3      2

如果有几个值符合模式,则选择第一个值。

共有2个答案

松献
2023-03-14

如果我对您的需求理解正确的话,您的源表包含MONTH和CODE列,并且您想要计算MEDIAN和MODE。

下面的查询使用移动窗口计算中位数和众数

-----------------------------------------------------
--Demo data
-----------------------------------------------------

CREATE TABLE #Data(
    [Month] INT NOT NULL,
    [Code] INT NOT NULL,
CONSTRAINT [PK_Data] PRIMARY KEY CLUSTERED 
(
    [Month] ASC
));

INSERT #Data
([Month],[Code])
VALUES
(1,0),
(2,3),
(3,2),
(4,2),
(5,2),
(6,5),
(7,3);

-----------------------------------------------------
--Query
-----------------------------------------------------

DECLARE @PrecedingRowsLimit INT = 2;

WITH [MPos] AS
(
    SELECT [R].[Month] 
        , [RB].[Month] AS [SubId]
        , [RB].[Code] 
        , ROW_NUMBER() OVER(PARTITION BY [R].[Month] ORDER BY [RB].[Code]) AS [RowNumberInPartition]
        , CASE 
            WHEN [R].[Count] % 2 = 1 THEN ([R].[Count] + 1) / 2
            ELSE NULL
            END AS [MedianPosition]
        , CASE 
            WHEN [R].[Count] % 2 = 0 THEN [R].[Count] / 2
            ELSE NULL
            END AS [MedianPosition1]
        , CASE 
            WHEN [R].[Count] % 2 = 0 THEN [R].[Count] / 2 + 1
            ELSE NULL
            END AS [MedianPosition2]
    FROM 
    (
        SELECT [RC].[Month] 
            , [RC].[RowNumber] 
            , CASE WHEN [RC].[Count] > @PrecedingRowsLimit + 1 THEN @PrecedingRowsLimit + 1 ELSE [RC].[Count] END AS [Count]
        FROM 
        (
            SELECT [Month] 
                , ROW_NUMBER() OVER(ORDER BY [Month]) AS [RowNumber]
                , ROW_NUMBER() OVER(ORDER BY [Month]) AS [Count]
            FROM #Data 
        ) [RC]
    ) [R]
        INNER JOIN #Data [RB]
        ON [R].[Month] >= [RB].[Month] 
            AND [RB].[Month] >= [R].[RowNumber] - @PrecedingRowsLimit
) 
SELECT DISTINCT [M].[Month] 
    , [ORIG].[Code] 
    , COALESCE([ME].[Code],([M1].[Code] + [M2].[Code]) / 2.0) AS [Median]
    , [MOD].[Mode] 
FROM [MPos] [M]
    LEFT JOIN [MPOS] [ME]
    ON [M].[Month] = [ME].[Month] 
        AND [M].[MedianPosition] = [ME].[RowNumberInPartition]
    LEFT JOIN [MPOS] [M1]
    ON [M].[Month] = [M1].[Month] 
        AND [M].[MedianPosition1] = [M1].[RowNumberInPartition]
    LEFT JOIN [MPOS] [M2]
    ON [M].[Month] = [M2].[Month] 
        AND [M].[MedianPosition2] = [M2].[RowNumberInPartition]
    INNER JOIN 
    (
        SELECT [MG].[Month] 
            , FIRST_VALUE([MG].[Code]) OVER (PARTITION BY [MG].[Month] ORDER BY [MG].[Count] DESC , [MG].[SubId] ASC) AS [Mode]
        FROM
        (
            SELECT [Month] , MIN([SubId]) AS [SubId], [Code]  , COUNT(1) AS [Count]
            FROM [MPOS] 
            GROUP BY [Month] , [Code] 
        ) [MG]
    ) [MOD]
    ON [M].[Month] = [MOD].[Month]
    INNER JOIN #Data [ORIG]
    ON [ORIG].[Month] = [M].[Month]
ORDER BY [M].[Month];
查宜民
2023-03-14

我彻底评论了我的代码。阅读我对我的模式计算的评论,让我知道它需要调整。总的来说,这是一个相对简单的查询。它只是有很多丑陋的子查询,并且有很多注释。检查一下:

DECLARE @Table TABLE ([Month] INT,[Code] INT);
INSERT INTO @Table
    VALUES  (1,0),
            (2,3),
            (3,2),
            (4,2), --Try commenting this out to test my special mode thingymajig
            (5,2),
            (6,5),
            (7,3);

WITH CTE
AS
(
SELECT  ROW_NUMBER() OVER (ORDER BY [Month]) row_num,
        [Month],
        CAST(Code AS FLOAT) Code
FROM @Table
)

SELECT [Month],
        Code,
        ISNULL((
                SELECT  CASE
                            --When there is only one previous value at row_num = 2, find Mean of first two codes
                            WHEN A.row_num = 2 THEN (LAG(B.code,1) OVER (ORDER BY [Code]) + B.Code)/2.0
                            --Else find middle code value of current and previous two rows
                            ELSE B.Code
                        END
                FROM CTE B 
                --How subquery relates to outer query
                WHERE B.row_num BETWEEN A.row_num - 2 AND A.row_num 
                ORDER BY B.[Code] 
                --Order by code and offset by 1 so don't select the lowest value, but fetch the one above the lowest value
                OFFSET 1 ROW FETCH NEXT 1 ROW ONLY),
        0) AS Median,
        --I did mode a little different
            --Instead of Avg(D.Code) you could list the values because with mode, 
                --If there's a tie with more than one of each number, you have multiple modes
                --Instead of doing that, I simply return the mean of the tied modes
                    --When there's one, it doesn't change anything.
                        --If you were to delete the month 4, then your number of Codes 2 and number of Codes 3 would be the same in the last row.
                        --Proper mode would be 2,3. I instead average them out to be 2.5.
        ISNULL((
                SELECT AVG(D.Code)
                FROM (
                    SELECT  C.Code,
                            COUNT(*) cnt,
                            DENSE_RANK() OVER (ORDER BY COUNT(*) DESC) dnse_rank
                    FROM CTE C
                    WHERE C.row_num <= A.row_num
                    GROUP BY C.Code
                    HAVING COUNT(*) > 1) D
                WHERE D.dnse_rank = 1),
        0) AS Mode
FROM CTE A

结果:

Month       Code                   Median                 Mode
----------- ---------------------- ---------------------- ----------------------
1           0                      0                      0
2           3                      1.5                    0
3           2                      2                      0
4           2                      2                      2
5           2                      2                      2
6           5                      2                      2
7           3                      3                      2
 类似资料:
  • 问题内容: 我的数据库应用程序将部署在不同时区的多个站点上。 我需要一个T-SQL函数,该函数将确定YTD计算本年1月1日午夜的UTC时间戳。所有数据都存储在UTC时间戳中。 例如,芝加哥是具有夏令时(DST)的UTC-6,如果该功能在2008年的任何时间在芝加哥运行,则该函数需要返回“ 2008-01-01 06:00:00”。如果明年在纽约(GMT-5 + DST)运行,则需要返回“ 2009

  • Postgres 9.1数据库包含表yksus1。。公共模式中的ykssu9。pgAdmin在下面的代码中显示了这些定义。如何将这些表移动到firma1模式? firma1模式中的其他表具有对这些表的外键引用。对这些表的外键引用仅来自firma1模式中的表。 其中一些表包含数据。如果表移动到firma1模式,外键引用也应更新到firma1。YKSUS表格。无法更改表结构。 看起来主键序列已经在fi

  • 问题内容: 我希望使用PATINDEX()将浮点数从某些varchars中拉出来。我知道在每个varchar字符串中,我只对存在的第一个浮点数感兴趣,但是它们的长度可能不同。 例如 我通常会用正则表达式来匹配它们 但是,我找不到PATINDEX接受的+运算符的等效项。因此,它们需要分别与以下项匹配: 有什么方法可以将这两个示例varchar与一个有效的PATINDEX模式进行匹配? 问题答案: 我

  • 使用 Chrome 开发者工具的Device Mode(设备模式)来构建移动优先,完全响应式的网站。了解如何使用它来模拟各种设备及其功能。警告: `Device Mode`(设备模式)可以近似估计您的网站在移动设备上的显示效果,但要获得完整的状况,你应该总是在真实的设备上测试你的网站。例如,DevTools无法模拟移动设备的性能特点。 简而言之 在不同的屏幕尺寸和分辨率,包括Retina显示屏,上

  • 问题内容: 使用MS SQL Server,以下工作正常: 但是,使用PostgreSQL,以下操作将失败: 错误:重复的键值违反了唯一约束“ tbl_test_pkey”详细信息:键(testkey)=(2)已存在。 我需要在一个表中增加一列的每个值,这是复合唯一约束的一部分。如何在一份声明中做到这一点? 谢谢 ! 编辑:如果您想知道为什么这样做(至少对我而言),那么这是一个更完整的方案。 我有

  • 用鼠标和方向键,简单地在Atom中移来移去非常容易,然而Atom有一些快捷键,可以让你把手一直放到键盘上,更快速地浏览文档。 首先,Atom自带许多Emacs的快捷键来浏览文档。要想上移或者下移一个字符,你可以按ctrl-P和ctrl-N。左移或右移一个字符,按ctrl-B和ctrl-F。这样等同于按下方向键,但是一些人不喜欢把他们的手移到方向键的位置。 除了单个字符的移动,还有一些其他的用于移动