oracle查询用户最后一次登录的信息

需求

查询用户最后一次登录的信息,每个用户只返回一条最后登录的记录

数据库

-- Create table
create table B_MAC_USB_KEY
(
  ID             VARCHAR2(64) not null,
  REMARK         VARCHAR2(2000),
  CREATE_BY      VARCHAR2(64),
  CREATE_DATE    DATE default sysdate,
  UPDATE_BY      VARCHAR2(64),
  UPDATE_DATE    DATE,
  STATUS         CHAR(1) default '0',
  SNO            VARCHAR2(20),
  MAC            VARCHAR2(100),
  BIND_NUM       NUMBER,
  LOGIN_DATE_MAC DATE,
  LOGIN_DATE_SNO DATE,
  NO             VARCHAR2(20)
)
tablespace YXDW
  pctfree 10
  initrans 1
  maxtrans 255
  storage
  (
    initial 64
    next 8
    minextents 1
    maxextents unlimited
  );
-- Add comments to the table 
comment on table B_MAC_USB_KEY
  is 'MAC和加密狗绑定关系表';
-- Add comments to the columns 
comment on column B_MAC_USB_KEY.REMARK
  is '备注';
comment on column B_MAC_USB_KEY.CREATE_BY
  is '创建人';
comment on column B_MAC_USB_KEY.CREATE_DATE
  is '创建时间';
comment on column B_MAC_USB_KEY.UPDATE_BY
  is '修改人';
comment on column B_MAC_USB_KEY.UPDATE_DATE
  is '修改时间';
comment on column B_MAC_USB_KEY.STATUS
  is '0:正常1:删除';
comment on column B_MAC_USB_KEY.SNO
  is '加密狗编号(usb外表的号码)';
comment on column B_MAC_USB_KEY.MAC
  is '机器码';
comment on column B_MAC_USB_KEY.BIND_NUM
  is '加密狗已绑定的MAC数量';
comment on column B_MAC_USB_KEY.LOGIN_DATE_MAC
  is 'mac登录时间';
comment on column B_MAC_USB_KEY.LOGIN_DATE_SNO
  is 'sno登录时间';
comment on column B_MAC_USB_KEY.NO
  is 'SN,加密狗内部序列号';

SQL语句

参考:https://ask.csdn.net/questions/744504

SELECT * FROM (
SELECT user_id , login_time ,row_number() over (partition by user_id order by login_time desc) MM from xxx)
WHERE mm = 1

正式语句

<sql id="userUsbKeyColumns">

        a.ID AS "id",
        a.BRAND AS "brand",                   <!--     终端品牌        -->
        a.TYPE AS "type",                         <!-- 终端类型   -->
        a.OWNER_ID AS "ownerId",          <!-- 公司ID  -->
        (
            select b.owner_name
            from B_OWNER b
            WHERE a.OWNER_ID = b.OWNER_ID
        )  AS "ownerName",                     <!-- 公司名  -->
        a.USER_ID AS "userId",                 <!-- 使用人id  -->
        (select b.u_cname from sys_user b where b.USERID = a.USER_ID) AS "userName",     <!--     使用人姓名        -->
        a.REMARK AS "remark",
        a.CREATE_BY AS "createBy",
        a.CREATE_DATE AS "createDate",
        a.UPDATE_BY AS "updateBy",
        a.UPDATE_DATE AS "updateDate",
        a.STATUS AS "status",      <!-- 0正常1停用  -->
        a.SNO AS "sno",            <!-- 加密狗设备编号(usb外表的号码)   -->
        a.NO AS "no",               <!-- SN,加密狗内部序列号  -->
        a.LOGIN_DATE_SNO AS "loginDateSno"                <!-- 加密狗登录时间  -->


    </sql>
 SELECT
        *
        FROM
        (
            SELECT
                <include refid="userUsbKeyColumns"/>
                ,ROW_NUMBER () OVER (
                PARTITION BY a.user_id
                ORDER BY  a.LOGIN_DATE_SNO DESC
            ) RNO
            FROM    B_USER_USB_KEY a
            <where>
                a.STATUS = #{status}
                <if test="ownerId != null and ownerId != '' ">
                    and a.OWNER_ID in (select owner_id from b_owner start with owner_id = #{ownerId} connect by prior owner_id=owner_pid)
                </if>

                <if test="sno != null and sno != '' ">
                    and a.sno = #{sno,jdbcType=VARCHAR}
                </if>
                <if test="userId != null and userId != '' ">
                    and a.USER_ID = #{userId}
                </if>
            </where>
        )
        WHERE
        RNO= 1

注意:ROW_NUMBER () OVER前要加“,”。

分析

Partition By a.user_id是按照a.user_id对查询结果分割(Partition的意思就是分割),a.user_id相同的在一起

Order By ORDER BY  a.LOGIN_DATE_SNO DESC是对分段的结果进行排序

对语句语句拆分分析

为了便于分析,现只查询上述语句的部分列

内层查询

SELECT
	A . ID AS "id",
	(
		SELECT
			b.owner_name
		FROM
			B_OWNER b
		WHERE
			A .OWNER_ID = b.OWNER_ID
	) AS "ownerName",-- 公司名  -->
	
	A .USER_ID AS userId,  -- 用户id  -->
	(
		SELECT
			b.u_cname
		FROM
			sys_user b
		WHERE
			b.USERID = A .USER_ID
	) AS "userName",    --     使用人姓名        -->
	
	A .STATUS AS "status",        -- 0正常1停用  -->
	
	A .SNO AS "sno",           -- 加密狗设备编号(usb外表的号码)   -->
	 
	A . NO AS "no",   -- SN,加密狗内部序列号  -->
	
	A .LOGIN_DATE_SNO AS "loginDateSno" -- 加密狗登录时间  -->
	,
	ROW_NUMBER () OVER (
		PARTITION BY A .user_id
		ORDER BY
			A .LOGIN_DATE_SNO DESC
	) RNO
FROM
	B_USER_USB_KEY A
WHERE
	A .STATUS = '0'

结果

通过 ROW_NUMBER () OVER (
        PARTITION BY A .user_id
        ORDER BY
            A .LOGIN_DATE_SNO DESC
    ) RNO

将user_id相同的放到一起,然后用LOGIN_DATE_SNO DESC进行排序,其中RNO表示user_id相同的记录有几个。每个user_id相同的截取片段都有一个第1名

如果不进行分割,即

 ROW_NUMBER () OVER (
        
        ORDER BY
            A .LOGIN_DATE_SNO DESC
    ) RNO

中没有PARTITION BY A .user_id

则查询结果为

查询结果按登录时间倒序,只有1个第1名 

外层语句

SELECT
	*
FROM
	(
		内层语句
	)
WHERE
	RNO = 1

RNO=1表示只显示内层语句中user_id相同的数据块的第一条记录

结果

这部分可以参考:https://www.sohu.com/a/280125372_120045344 

ORACLE的PARTITION BY 用法

是什么

 

       Parttion by 关键字是Oracle中分析性函数over的一部分,Oracle从8.1.6开始提供分析函数,它和聚合函数不同的地方在于它能够返回一个分组中的多条记录,而聚合函数一般只有一条反映统计值的结果。

干什么

分析函数用于计算基于组的某种聚合值,常用于大型项目中的统计分析及相关功能

有那些

--row_number() 顺序排序
row_number() over(partition by ... order by ...)
--rank() (跳跃排序,如果有两个第一级别时,接下来是第三级别)
rank() over(partition by ... order by ...)
--dense_rank()(连续排序,如果有两个第一级别时,接下来是第二级)
dense_rank() over(partition by ... order by ...)
--求分组后的总数。
count() over(partition by ... order by ...)
--求分组后的最大值。
max() over(partition by ... order by ...)
--求分组后的最小值。
min() over(partition by ... order by ...)

sum() over(partition by ... order by ...)
--求分组后的平均值。
avg() over(partition by ... order by ...)
--第一个
first_value() over(partition by ... order by ...)
--最后一个
last_value() over(partition by ... order by ...)
--取出前n行数据。 
lag() over(partition by ... order by ...)
--取出后n行数据。
lead() over(partition by ... order by ...)
--Ratio_to_report() 括号中就是分子,over() 括号中就是分母。
ratio_to_report() over(partition by ... order by ...)
percent_rank() over(partition by ... order by ...)

实例1

查询出每个部门工资最低的员工编号【每个部门可能有两个最低的工资员工】  

create table TSALER
(
  userid NUMBER(10),
  salary NUMBER(10),
  deptid NUMBER(10)
)

-- Add comments to the columns 
comment on column TSALER.userid
  is '员工ID';
comment on column TSALER.salary
  is '工资';
comment on column TSALER.deptid
  is '部门ID';

insert into TSALER (工号, 工资, 部门编号)
values (1, 200, 1);

insert into TSALER (工号, 工资, 部门编号)
values (2, 2000, 1);

insert into TSALER (工号, 工资, 部门编号)
values (3, 200, 1);

insert into TSALER (工号, 工资, 部门编号)
values (4, 1000, 2);

insert into TSALER (工号, 工资, 部门编号)
values (5, 1000, 2);

insert into TSALER (工号, 工资, 部门编号)
values (6, 3000, 2);

   

查询结果:

方法一

SELECT
	tsaler.*
FROM
	tsaler
INNER JOIN (
	SELECT
		MIN (salary) AS salary,
		deptid
	FROM
		tsaler
	GROUP BY
		deptid
) c ON tsaler.salary = c.salary
AND tsaler.deptid = c.deptid

方法二

SELECT
	*
FROM
	tsaler
INNER JOIN (
	SELECT
		MIN (salary) AS salary,
		deptid
	FROM
		tsaler
	GROUP BY
		deptid
) c USING (salary, deptid)

方法三

 

--row_number() 顺序排序
SELECT
	ROW_NUMBER () OVER (
		PARTITION BY deptid
		ORDER BY
			salary
	) my_rank,
	deptid,
	USERID,
	salary
FROM
	tsaler;

--rank() (跳跃排序,如果有两个第一级别时,接下来是第三级别)
SELECT
	RANK () OVER (
		PARTITION BY deptid
		ORDER BY
			salary
	) my_rank,
	deptid,
	USERID,
	salary
FROM
	tsaler;

--dense_rank()(连续排序,如果有两个第一级别时,接下来是第二级)
SELECT
	DENSE_RANK () OVER (
		PARTITION BY deptid
		ORDER BY
			salary
	) my_rank,
	deptid,
	USERID,
	salary
FROM
	tsaler;

-------方案3解决方案
SELECT
	*
FROM
	(
		SELECT
			RANK () OVER (
				PARTITION BY deptid
				ORDER BY
					salary
			) my_rank,
			deptid,
			USERID,
			salary
		FROM
			tsaler
	)
WHERE
	my_rank = 1;

SELECT
	*
FROM
	(
		SELECT
			DENSE_RANK () OVER (
				PARTITION BY deptid
				ORDER BY
					salary
			) my_rank,
			deptid,
			USERID,
			salary
		FROM
			tsaler
	)
WHERE
	my_rank = 1;

参考:https://www.cnblogs.com/jak-black/p/4210653.html 

实例2

需求:对每一个类型进行求和并且求该类型所占的比例

SELECT
	T .CHANNEL AS PATTERN,
	COUNT (T .TRANSACTIONKEY) AS T_COUNT,
	SUM (T .AMT) AS T_AMT,
	ROUND (
		100 * SUM (T .AMT) / SUM (SUM(T .AMT)) OVER (PARTITION BY 1),
		2
	) AS AMT_PERCENT,
	ROUND (
		100 * COUNT (T .TRANSACTIONKEY) / SUM (COUNT(T .TRANSACTIONKEY)) OVER (PARTITION BY 1),
		2
	) AS COUNT_PERCENT
FROM
	XX (表名) T
WHERE
	T .PARTY_ID = '100579050'
GROUP BY
	T .CHANNEL

 

实验数据 

create table T2_TEMP(
    NAME varchar2(10) primary key,
    CLASS varchar2(10),
    SROCE NUMBER
)

insert into T2_TEMP (NAME, CLASS, SROCE)
values ('cfe', '2', 74);

insert into T2_TEMP (NAME, CLASS, SROCE)
values ('dss', '1', 95);

insert into T2_TEMP (NAME, CLASS, SROCE)
values ('ffd', '1', 95);

insert into T2_TEMP (NAME, CLASS, SROCE)
values ('fda', '1', 80);

insert into T2_TEMP (NAME, CLASS, SROCE)
values ('gds', '2', 92);

insert into T2_TEMP (NAME, CLASS, SROCE)
values ('gf', '3', 99);

insert into T2_TEMP (NAME, CLASS, SROCE)
values ('ddd', '3', 99);

insert into T2_TEMP (NAME, CLASS, SROCE)
values ('adf', '3', 45);

insert into T2_TEMP (NAME, CLASS, SROCE)
values ('asdf', '3', 55);

insert into T2_TEMP (NAME, CLASS, SROCE)
values ('3dd', '3', 78);

1、over函数的写法:

  over(partition by class order by sroce) 按照sroce排序进行累计,order by是个默认的开窗函数,按照class分区。

  2、开窗的窗口范围:

  over(order by sroce range between 5 preceding and 5 following):窗口范围为当前行数据幅度减5加5后的范围内的。

  over(order by sroce rows between 5 preceding and 5 following):窗口范围为当前行前后各移动5行。

  3、与over()函数结合的函数的介绍

  (1)、查询每个班的第一名的成绩:如下 

SELECT
	*
FROM
	(
		SELECT
			T . NAME,
			T . CLASS,
			T .sroce,
			RANK () OVER (
				PARTITION BY T . CLASS
				ORDER BY
					T .sroce DESC
			) mm
		FROM
			T2_TEMP T
	)
WHERE
	mm = 1;

  结果为:

dss        1        95        1
ffd        1        95        1
gds        2        92        1
gf         3        99        1
ddd        3        99        1

 

  注意:在求第一名成绩的时候,不能用row_number(),因为如果同班有两个并列第一,row_number()只返回一个结果。

SELECT
	*
FROM
	(
		SELECT
			T . NAME,
			T . CLASS,
			T .sroce,
			ROW_NUMBER () OVER (
				PARTITION BY T . CLASS
				ORDER BY
					T .sroce DESC
			) mm
		FROM
			T2_TEMP T
	)
WHERE
	mm = 1;

  结果为:

dss      1        95        1  
gfs      2        92        1
ddd      3        99        1 

  可以看出,本来第一名是两个人的并列,结果只显示了一个。

  (2)、rank()和dense_rank()可以将所有的都查找出来,rank可以将并列第一名的都查找出来rank()和dense_rank()区别:rank()是跳跃排序,有两个第二名时接下来就是第四名。

  求班级成绩排名:

SELECT
	T . NAME,
	T . CLASS,
	T .sroce,
	RANK () OVER (
		PARTITION BY T . CLASS
		ORDER BY
			T .sroce DESC
	) mm
FROM
	T2_TEMP T;

  查询结果:

 

dss        1        95        1
ffd        1        95        1
fda        1        80        3
gds        2        92        1
cfe        2        74        2
gf         3        99        1
ddd        3        99        1
3dd        3        78        3
asdf       3        55        4
adf        3        45        5

 

  dense_rank()是连续排序,有两个第二名时仍然跟着第三名

SELECT
	T . NAME,
	T . CLASS,
	T .sroce,
	DENSE_RANK () OVER (
		PARTITION BY T . CLASS
		ORDER BY
			T .sroce DESC
	) mm
FROM
	T2_TEMP T;

  查询结果:

 

dss        1        95        1
ffd        1        95        1
fda        1        80        2 
gds        2        92        1
cfe        2        74        2
gf         3        99        1
ddd        3        99        1
3dd        3        78        2
asdf       3        55        3
adf        3        45        4

 

  3、sum()over()的使用

  根据班级进行分数求和

SELECT
	T . NAME,
	T . CLASS,
	T .sroce,
	SUM (T .sroce) OVER (
		PARTITION BY T . CLASS
		ORDER BY
			T .sroce DESC
	) mm
FROM
	T2_TEMP T;

 

dss        1        95        190  --由于两个95都是第一名,所以累加时是两个第一名的相加
ffd        1        95        190 
fda        1        80        270  --第一名加上第二名的
gds        2        92        92
cfe        2        74        166
gf         3        99        198
ddd        3        99        198
3dd        3        78        276
asdf       3        55        331
adf        3        45        376

 

  4、first_value() over()和last_value() over()的使用 

SELECT
	T . NAME,
	T . CLASS,
	T .sroce,
	FIRST_VALUE (T .sroce) OVER (
		PARTITION BY T . CLASS
		ORDER BY
			T .sroce DESC
	) mm
FROM
	T2_TEMP T;

SELECT
	T . NAME,
	T . CLASS,
	T .sroce,
	LAST_VALUE (T .sroce) OVER (
		PARTITION BY T . CLASS
		ORDER BY
			T .sroce DESC
	) mm
FROM
	T2_TEMP T;

  分别求出第一个和最后一个成绩。

  5、sum() over()的使用

SELECT
	T . NAME,
	T . CLASS,
	T .sroce,
	SUM (T .sroce) OVER (
		PARTITION BY T . CLASS
		ORDER BY
			T .sroce DESC
	) mm
FROM
	T2_TEMP T;

  求出班级的总分。  

  6、over partition by与group by的区别:

  group by是对检索结果的保留行进行单纯分组,一般和聚合函数一起使用例如max、min、sum、avg、count等一块用。partition by虽然也具有分组功能,但同时也具有其他的高级功能。

参考:https://www.cnblogs.com/lcngu/p/5335170.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章