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

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