SQL ストアド プロシージャのプログラミングに関する注意事項


1 はじめに

最近、統計データ用の簡単なストアド プロシージャを作成しました。ここにメモを取り、このストアド プロシージャで使用されるいくつかの基本的な知識ポイントの簡単な要約を作成します。(注: ここのデータベースは postgresql を使用しています)


2. 背景

表 1: The third party service writes data to Table 1 every 10 minutes. 書き込みごとに新しいデータがテーブルに挿入されるため、デバイスは毎日テーブルに複数のデータを保持します。

CREATE TABLE tb_check (
	id serial NOT NULL, 			--自增ID
	devicecode text NULL, 			--设备ID 
	dateid int4 NULL,				--日期,格式: 20220509 
	num int4 NULL DEFAULT 0,		--准确数量
	totalnum int4 NULL DEFAULT 0 	--检测总数
);


表 2: 表 1 のデータの統計を作成し、「デバイス + 日付」をキー値として保存します。つまり、デバイスは 1 日に 1 つのデータのみをテーブルに保存し、検出精度を計算します。

create table if not exists tb_check_statistics(
	id serial NOT NULL,				--自增ID
	devicecode text NULL,			--设备ID 
	dateid int4 NULL,				--日期,格式: 20220509 
	num int4 NULL DEFAULT 0,		--准确数量
	totalnum int4 NULL DEFAULT 0,	--检测总数
	result text NULL,				--准确率(准确数量/检测总数,格式:0.7500)
	jczt int4 NULL 					--1:正常;2:异常(大于等于75%为正常,否则为异常)
);

ターゲット データ グラフ:
ここに画像の説明を挿入

3. ストアド プロシージャの基本形式を作成する

drop function if exists testprocess;
CREATE OR REPLACE FUNCTION testprocess()
RETURNS INTEGER AS
$BODY$
	declare 
		--声明变量
		
	begin 
		--操作模块
		RAISE NOTICE 'this is a test process, date:%', 20220509;
		return 0; --这里要和第二行定义的返回值对应,否则会报错
	end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;

実行: select * from testprocess();、出力結果は下図のようになります
ここに画像の説明を挿入


4.構造定義

データ構造の定義は、通常のプログラミングと似ています. 次のように、2 つのデータ構造を定義します。1 つはテーブル フィールド データを格納するためのもので、もう 1 つは時間を格納するためのものです。

--表字段结构体
CREATE TYPE check_type AS (
    devicecode     	text,
    dateid          INTEGER,
	num          	INTEGER,
	totalnum        INTEGER,
	result     		text,      	
	jczt     		INTEGER		 	
);

--时间结构体
CREATE TYPE time_item AS (
    currentdate text, 	--'2022-04-06 13:37:17'
	currentyear text,	--'2022'
	currentmonth text,	--'04'
	currentday text,	--'06'
	timestr text,		--'20220406'
	timeid INTEGER		--20220406
);


5. 変数を宣言する

変数のタイプには、テーブル、構造体、配列、整数、ブール、文字などがあります。

declare 
		data_list11 tb_check; 		--为表1声明一个变量,用于读取表数据时提取参数值
		timeitem time_item; 		--时间结构体
		ivitem check_type;			--表字段结构体
		ivitemarray  check_type[];	--表字段数组
		countid INTEGER := 0;		
		iindex INTEGER := 0;
		isflag boolean := false;
		radius NUMERIC := 1.0;
		caluresult NUMERIC := 1.0;
		datetime NUMERIC := 0;


6. システム時刻の取得とフォーマット変換


1. システム時刻を取得する
現在の時刻を取得し、select...into... を使用して変数に代入します。

select floor(extract(epoch from now())) into datetime;	--单位:秒


2. 時間形式の変換

timeitem time_item;				--定义结构体变量
select to_char(to_timestamp(datetime), 'yyyy-mm-dd hh24:mi:ss') into timeitem.currentdate; --'2022-04-06 13:37:17' 
timeitem.currentdate = substring(timeitem.currentdate from 1 for 10); 		--'2022-04-06'
select split_part(timeitem.currentdate ,'-', 1) into timeitem.currentyear; 	--'2022'
select split_part(timeitem.currentdate ,'-', 2) into timeitem.currentmonth;	--'04'
select split_part(timeitem.currentdate ,'-', 3) into timeitem.currentday;	--'06'
select timeitem.currentyear || timeitem.currentmonth || timeitem.currentday into timeitem.timestr; -- '20220406'
select timeitem.timestr::INTEGER into timeitem.timeid; --20220406


7. テーブル tb_check データの読み取り

テーブル tb_check データを循環的に読み取り、配列に格納します。

--以天为条件循环读取一天的数据, 提取数据并存储到数组中
for data_list11 in select * from tb_check  where dateid=timeitem.timeid loop 
	--1)循环遍历数组ivitemarray,以devicecode为唯一标识,查看数组中是否已经存在该设备的值,如果存在则更新;
	isflag := false;
	if countid > 0 then
		FOR n IN 1..countid LOOP --循环数组 
			if ivitemarray[n].devicecode = data_list11.devicecode then --数组中可以查到,则更新数据
				ivitem.devicecode = data_list11.devicecode;
				ivitem.dateid = data_list11.dateid;
				ivitem.num = ivitemarray[n].num+data_list11.num;
				ivitem.totalnum = ivitemarray[n].totalnum+data_list11.totalnum;
				ivitemarray[n] = ivitem; 			--覆盖原值

				isflag := true;
				EXIT;					--找到则退出循环
			end if;
		END LOOP;	
	end if;
	
	--2)若不存在则添加到数组	
	if isflag = false then --数组中查询不到,则添加到数组
		ivitem.devicecode = data_list11.devicecode;
		ivitem.dateid = data_list11.dateid;
		ivitem.num = data_list11.num;
		ivitem.totalnum = data_list11.totalnum;
		countid := countid+1;
		ivitemarray[countid] = ivitem;	
	end if;
end loop; 


8. データがテーブル tb_check_statistics に挿入されます

配列をトラバースし、データをテーブル tb_check_statistics に挿入します。

if countid > 0 then
	FOR n IN 1..countid LOOP
		--准确率计算
		--这里省略,不做说明........
		
		--插入表中
		insert into tb_check_statistics
		(
			devicecode, dateid, num, totalnum, result, jczt
		)values
		(
			ivitemarray[n].devicecode, ivitemarray[n].dateid, ivitemarray[n].num, 
			ivitemarray[n].totalnum, ivitemarray[n].result, ivitemarray[n].jczt
		);
		
	END LOOP;
end if;


9. データの挿入: 存在する場合は更新、存在しない場合は挿入

トピック外の要件: データを挿入する必要があるテーブルがあり、既に存在する場合は更新され、存在しない場合は挿入されます。
Mysql と postgresql は実装が異なります。ここでは、 postgresql のON CONFLICT使用。


1. 使用ON CONFLICTにはテーブルの特別な要件があります。つまり、テーブルは「制約の一意の識別子」を指定する必要があります。

create table if not exists tb_check_ex(
	id serial NOT NULL,
	devicecode text NULL,
	dateid int4 NULL,
	t11 int4 NULL DEFAULT 0,		
	t12 int4 NULL DEFAULT 0,		
	t13 text NULL,					
	t14 int4 NULL, 					
	t21 int4 NULL DEFAULT 0,		
	t22 int4 NULL DEFAULT 0,		
	t23 text NULL,					
	t24 int4 NULL, 					
	unique(dateid,devicecode) --需要特别指定 约束唯一标识
);

制約の一意の識別子は、単一のフィールドまたは複数のフィールドの組み合わせにすることができます. ここでは、'dateid と devicecode' の 2 つのフィールドが制約の一意の識別子として使用されます.



2.挿入/更新ステートメント

--insert t11, t12, t13, t14
insert into tb_check_ex
(
	devicecode, dateid, t11, t12, t13, t14
)values
(
	ivitemarray[n].devicecode, ivitemarray[n].dateid, ivitemarray[n].num, 
	ivitemarray[n].totalnum, ivitemarray[n].result, ivitemarray[n].jczt
)ON CONFLICT (devicecode,dateid) 
DO UPDATE SET t11=ivitemarray[n].num,t12=ivitemarray[n].totalnum,t13=ivitemarray[n].result,t14=ivitemarray[n].jczt;

--insert t21, t22, t23, t24
insert into tb_check_ex
(
	devicecode, dateid, t21, t22, t23, t24
)values
(
	ivitemarray[n].devicecode, ivitemarray[n].dateid, ivitemarray[n].num, 
	ivitemarray[n].totalnum, ivitemarray[n].result, ivitemarray[n].jczt
)ON CONFLICT (devicecode,dateid) 
DO UPDATE SET t21=ivitemarray[n].num,t22=ivitemarray[n].totalnum,t23=ivitemarray[n].result,t24=ivitemarray[n].jczt;

このように、同一の devicecode と dateid の場合、上記 2 つの insert ステートメントを実行した後、1 つのデータのみがテーブルに保存されます。
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/locahuang/article/details/124669902