Oracle高级PL/SQL

Oracle高级PL/SQL

数据的批量处理

游标在处理大批量数据的时候,仅仅启用单核心,会带来性能下降。

通过增强循环语句FORALL和批量绑定语句BULK能够大幅提升性能。

批量绑定

ORACLE的官方文档释意

Oracle Database uses two engines to run PL/SQL units. The PL/SQL engine runs the procedural statements and the SQL engine runs the SQL statements. Every SQL statement causes a context switch between the two engines. You can greatly improve the performance of your database application by minimizing the number of context switches for each PL/SQL unit.

Oracle数据库使用PL/SQL引擎解析过程和函数,使用SQL引擎解析SQL语句,两个引擎相互配合。每个SQL语句都会引起两个引擎之间的上下文切换。如果能够降低两个引擎之间的频繁切换,那么就能最大限度地提升性能,而使用批量绑定语法BULK是能实现。

好处:

BULK 是一次绑定整个集合,通过一次性发送SQL语句供SQL引擎解析的语句。受 SQL 语句影响的行越多,大容量绑定带来的性能提升就越大。考虑使用批量绑定来提高 DML 和 SELECT INTO 引用集合的语句以及 FOR 引用集合并返回 DML 的循环的性能。

实例1:通过bulk批量更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
DECLARE
TYPE numlist IS VARRAY (100) OF NUMBER;
id NUMLIST := NUMLIST(7902, 7698, 7839);
BEGIN
-- Efficient method, using bulk bind:

FORALL i IN id.FIRST..id.LAST
UPDATE EMPLOYEES
SET SALARY = 1.1 * SALARY
WHERE MANAGER_ID = id(i);

-- Slower method:

FOR i IN id.FIRST..id.LAST LOOP
UPDATE EMPLOYEES
SET SALARY = 1.1 * SALARY
WHERE MANAGER_ID = id(i);
END LOOP;
END;
/

实例2:配合ReturningFORALL语句实现批量更新

需要返回值的情况下,使用 Returning 指定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
DECLARE
TYPE emp_list IS VARRAY(100) OF EMPLOYEES.EMPLOYEE_ID%TYPE;
empids emp_list := emp_list(182, 187, 193, 200, 204, 206);

TYPE bonus_list IS TABLE OF EMPLOYEES.SALARY%TYPE;
bonus_list_inst bonus_list;

BEGIN
-- Efficient method, using bulk bind:

FORALL i IN empids.FIRST..empids.LAST
UPDATE EMPLOYEES
SET SALARY = 0.1 * SALARY
WHERE EMPLOYEE_ID = empids(i)
RETURNING SALARY BULK COLLECT INTO bonus_list_inst;

-- Slower method:

FOR i IN empids.FIRST..empids.LAST LOOP
UPDATE EMPLOYEES
SET SALARY = 0.1 * SALARY
WHERE EMPLOYEE_ID = empids(i)
RETURNING SALARY INTO bonus_list_inst(i);
END LOOP;
END;
/

数据批量更新

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
OPEN cur_ap_data;
LOOP
FETCH cur_ap_data BULK COLLECT
INTO record_update LIMIT 1000;

FORALL i IN 1 .. record_update.count
UPDATE inf_gl_voucheritemass
SET MD_CUSTOMER = NULL,
MD_supplier = record_update(i).MD_CUSTOMER,
MD_CFITEM = record_update(i).MD_CFITEM,
MD_INDUSTRY = record_update(i).MD_INDUSTRY,
unitcode = record_update(i).unitcode,
vchrnum = record_update(i).vchrnum
WHERE 1 = 1
AND id = record_update(i).id
AND vchrid = record_update(i).vchrid;

COMMIT;

EXIT WHEN cur_ap_data%NOTFOUND;
END LOOP;
CLOSE cur_ap_data;

【注意】对于头行关联的两张表的数据同步,依然要使用游标进行处理

此外,BULK INTO还能用于DML语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
DECLARE
TYPE emp_rec_type IS RECORD
(
empno emp.empno%TYPE,
ename emp.ename%TYPE,
hiredate emp.hiredate%TYPE
);
TYPE nested_emp_type IS TABLE OF emp_rec_type;
emp_tab nested_emp_type;
BEGIN
DELETE FROM emp WHERE deptno = 20
RETURNING empno, ename, hiredate -- 使用returning 返回这几个列
BULK COLLECT INTO emp_tab; -- 将返回的列的数据批量插入到集合变量

DBMS_OUTPUT.put_line( '删除 ' || SQL%ROWCOUNT || ' 行记录' );
COMMIT;

IF emp_tab.COUNT > 0 THEN -- 当集合变量不为空时,输出所有被删除的元素
FOR i IN emp_tab.FIRST .. emp_tab.LAST LOOP
DBMS_OUTPUT.PUT_LINE('当前记录:'
|| emp_tab( i ).empno || CHR( 9 )
|| emp_tab( i ).ename || CHR( 9 )
|| emp_tab( i ).hiredate
|| ' 已被删除' );
END LOOP;
END IF;
END;

数据批量删除

通过forall增强循环删除本地大量的数据

实例:根据id来删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
DECLARE
l_cnt NUMBER := 0;
l_start NUMBER;

TYPE id_type IS TABLE OF VARCHAR2(255);
id_tbl id_type;

CURSOR cur IS
SELECT t.erp_his_id
FROM gj_erp_bas_inv_item_his t
WHERE 1 = 1
AND t.creation_date <= to_date('2019-12-31', 'yyyy-mm-dd');
BEGIN
OPEN cur;
LOOP

FETCH cur BULK COLLECT
INTO id_tbl LIMIT 1000;
EXIT WHEN id_tbl.count = 0;

DBMS_OUTPUT.put_line(id_tbl.count || ' rows');

l_start := DBMS_UTILITY.get_time;
FORALL i IN id_tbl.first .. id_tbl.last
DELETE FROM gj_erp_bas_inv_item_his WHERE erp_his_id = id_tbl(i);
COMMIT;

DBMS_OUTPUT.put_line('Delete : ' || (DBMS_UTILITY.get_time - l_start));

--dbms_lock.sleep(3); 线程休眠需要dba权限
END LOOP;
END;

数据批量插入

性能比较

实验1:大批量复杂查询

以oracle ebs环境为例,同步应付会计分录,数据集为33万+,获取数据集9秒/100条

循环table类型

使用bulk抓取数据到table类型中,然后循环table,每2000条数据提交一次

1.定义table类型

1
2
TYPE l_rec_line IS TABLE OF INF_GL_VOUCHERITEMASS%ROWTYPE;
l_rec_line_list l_rec_line;

2.执行阶段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
OPEN cur_voucher_lines;
LOOP
FETCH cur_voucher_lines BULK COLLECT
INTO l_rec_line_list LIMIT 1000;

FORALL i IN l_rec_line_list.first .. l_rec_line_list.last
INSERT INTO INF_GL_VOUCHERITEMASS VALUES l_rec_line_list (i);

-- 每1000条提交一次
COMMIT;

EXIT WHEN cur_voucher_lines%NOTFOUND;
END LOOP;
CLOSE cur_voucher_lines;

3.实验结果

耗时1.5小时,共同步48000条数据,平均每分钟533条数据。

【注意】这里都是按整数批量插入,可能会存在抓取数据集的时候停止同步的过程。

游标单行循环

经典游标单循环

1.实现

1
2
3
FOR rec_line IN cur_voucher_lines LOOP
...
END LOOP;

2.实验结果

耗时1.5小时,共同步49144条数据,平均每分钟546条数据。

实验2:化解复杂查询

以oracle ebs环境为例,头和行的全部会计分类同步。

对于行上要获取头上的数据时,除了使用游标进行双重循环来保证行上能获得头上的数据以外,

还可以通过 先同步行,然后同步头,最后头和行关联,批量update的方式达到数据的最终一致性。

游标双重循环

同步应收发票、应收收款、应付发票、应付付款

1
2
3
4
5
6
7
8
9
for rec_header in cur_header loop
-- 同步头数据
...

for rec_line in cur_line loop
-- 同步行数据
...
end loop;
end loop;

结果:

image.png

批量插入与更新

包定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CREATE OR REPLACE PACKAGE gj_congl_voucher_sync_v2 IS

/*==================================================
Procedure Name :
main
Description:
This procedure perform to 集团币种同步
Argument:
errbuf : concurrent return message buffer
retcode : concurrent return status code
0 success / 1 warning / 2 error
p_book_id : 账簿ID
p_date_from : 日期从
p_date_to : 至
p_item_from : 研发项目从
p_item_to : 至

History:
1.00 2020-09-02 LIYIHUI Creation
==================================================*/
PROCEDURE main(errbuf OUT VARCHAR2, retcode OUT VARCHAR2);

END gj_congl_voucher_sync_v2;

包内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
CREATE OR REPLACE PACKAGE BODY gj_congl_voucher_sync_v2 IS

-- Set Who
g_conc_request_id NUMBER := fnd_global.conc_request_id;
g_log_msg VARCHAR2(255);

-- Global Parameter
g_pkg_name VARCHAR2(255) := 'gj_congl_voucher_sync';
g_error VARCHAR2(20) := fnd_api.g_ret_sts_error;

/************************************************
名称:output
功能描述:
输出消息

修订记录:
版本号 编辑时间 编辑人 修改描述
1.0.0 2020-09-02 LIYIHUI 1.创建

参数描述:
p_message IN 传入参数1
**************************************************/
PROCEDURE output(p_message_text VARCHAR2) IS
BEGIN
IF g_conc_request_id > 0 THEN
fnd_file.put_line(fnd_file.log, p_message_text);
ELSE
dbms_output.put_line(to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') ||
' >>>>>> ' || p_message_text);
END IF;
END output;

/*==================================================
Procedure Name :
log
Description:
This procedure perform to 日志
Argument:
p_message : 消息
History:
1.00 2020-09-02 LIYIHUI Creation
==================================================*/
PROCEDURE log(p_message_text VARCHAR2) IS
BEGIN
IF g_conc_request_id > 0 THEN
fnd_file.put_line(fnd_file.log,
to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') ||
' >>>>>> ' || p_message_text);
ELSE
dbms_output.put_line(to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') ||
' >>>>>> ' || p_message_text);
END IF;
END log;

/*==================================================
Procedure Name :
raise_exception
Description:
This procedure perform to 抛出异常
Argument:
x_return_status : Input Status : E(Error)/U(Unexception)/S(Success)
History:
1.00 2020-09-02 LIYIHUI Creation
==================================================*/
PROCEDURE raise_exception(x_return_status IN VARCHAR2) IS
BEGIN
IF (x_return_status = fnd_api.g_ret_sts_unexp_error) THEN
RAISE fnd_api.g_exc_unexpected_error;
ELSIF (x_return_status = fnd_api.g_ret_sts_error) THEN
RAISE fnd_api.g_exc_error;
END IF;
END raise_exception;

/*==================================================
Procedure Name :
raise_exception
Description:
This procedure perform to 期初凭证同步
Argument:
x_return_status : Input Status : E(Error)/U(Unexception)/S(Success)
History:
1.00 2020-11-17 LIYIHUI Creation
==================================================*/
PROCEDURE sp_nc_voucher(x_return_status OUT NOCOPY VARCHAR2,
x_msg_count OUT NOCOPY NUMBER,
x_msg_data OUT NOCOPY VARCHAR2) IS
l_api_name CONSTANT VARCHAR2(30) := 'ins_sp_nc_voucher';
l_savepoint_name CONSTANT VARCHAR2(30) := 'sp_nc_voucher';

BEGIN
-- start activity to create savepoint, check compatibility
-- and initialize message list, include debug message hint to enter api
x_return_status := cux_api.start_activity(p_pkg_name => g_pkg_name,
p_api_name => l_api_name,
p_savepoint_name => l_savepoint_name);
raise_exception(x_return_status => x_return_status);

INSERT INTO INF_GL_VOUCHER
(id,
bookcode,
unitcode,
acctyear,
acctperiod,
vchrtypecode,
vchrnum,
creator,
createtime,
createdate,
postdate,
postor,
cashier,
cashdate,
attachment,
adjperiodflag,
tempflag,
offsetstate,
offsetid,
blueoffsetstate,
blueoffsetid,
def_01,
def_02,
def_03,
def_04,
def_05,
def_06,
def_07,
def_08,
def_09,
update_time)
SELECT * FROM INF_GL_VOUCHER_NC;

INSERT INTO INF_GL_VOUCHERITEMASS
(id,
bookcode,
unitcode,
subjectcode,
subjectname,
ori_subjectcode,
ori_subjectname,
currencycode,
vchrid,
acctyear,
acctperiod,
rowflag,
vchrtypecode,
vchrnum,
createdate,
postflag,
debit,
credit,
orgnd,
orgnc,
qty,
price,
exchrate,
digest,
bizdate,
expiredate,
md_staff,
md_department,
md_custsupplier,
md_customer,
md_supplier,
md_industry,
md_cfitem,
md_marbasclass,
md_measurement,
md_bankaccount,
md_reason,
md_project,
md_contract,
md_inde_project,
md_fundplan,
md_financing,
def_01,
def_02,
def_03,
def_04,
def_05,
def_06,
def_07,
def_08,
def_09,
update_time)
SELECT * FROM inf_gl_voucheritemass_NC;

COMMIT;

-- API end body
-- end activity, include debug message hint to exit api
x_return_status := cux_api.end_activity(p_pkg_name => g_pkg_name,
p_api_name => l_api_name,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);
raise_exception(x_return_status => x_return_status);
EXCEPTION
WHEN fnd_api.g_exc_error THEN
x_return_status := cux_api.handle_exceptions(p_pkg_name => g_pkg_name,
p_api_name => l_api_name,
p_savepoint_name => l_savepoint_name,
p_exc_name => cux_api.g_exc_name_error,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);
WHEN fnd_api.g_exc_unexpected_error THEN
x_return_status := cux_api.handle_exceptions(p_pkg_name => g_pkg_name,
p_api_name => l_api_name,
p_savepoint_name => l_savepoint_name,
p_exc_name => cux_api.g_exc_name_unexp,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);
WHEN OTHERS THEN
x_return_status := cux_api.handle_exceptions(p_pkg_name => g_pkg_name,
p_api_name => l_api_name,
p_savepoint_name => l_savepoint_name,
p_exc_name => cux_api.g_exc_name_others,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);
END sp_nc_voucher;

/**
* @Author : KALOSORA
* @Purpose : 同步会计分录行,根据行的accounting_date反过来匹配头的数据
* 可能会存在如下情况
* ap_invoices_all.gl_date < 2020-01-01
* 但此时存在冲销、取消的业务
* xla_ae_lines.accounting_date > 2020-01-01
*
* @Param :x_return_status 返回的参数状态
* @Param :x_return_status 返回的消息
* @Version : 1.0
* @Since : 2021/1/19 9:12:35
*/
PROCEDURE sp_vchrnum_lines(x_return_status OUT NOCOPY VARCHAR2,
x_msg_count OUT NOCOPY NUMBER,
x_msg_data OUT NOCOPY VARCHAR2) IS
l_api_name CONSTANT VARCHAR2(100) := 'sp_vchrnum_lines';

TYPE l_rec_line IS TABLE OF INF_GL_VOUCHERITEMASS%ROWTYPE;
l_rec_line_list l_rec_line;

CURSOR cur_voucher_lines IS
SELECT to_char(xal.AE_HEADER_ID) || LPAD(xal.AE_LINE_NUM, 4, 0) id,
'GJ_CN_01' BOOKCODE,
'PENDING' UNITCODE,
--xep.LEGAL_ENTITY_IDENTIFIER UNITCODE,
(SELECT t.org_coa_acc_code
FROM gj_organization_coa_flex_value t
WHERE 1 = 1
AND t.COA_ACC_CODE = gcck.segment3) SUBJECTCODE,
(SELECT t.org_coa_acc_desc
FROM gj_organization_coa_flex_value t
WHERE 1 = 1
AND t.COA_ACC_CODE = gcck.segment3) SUBJECTNAME,
gcck.segment3 ORI_SUBJECTCODE,
gj_account_details_rpt.get_flex_description('GJ_COA_ACC',
gcck.segment3) ORI_SUBJECTNAME,
xal.CURRENCY_CODE CURRENCYCODE,
ora_hash(xte.entity_code) || to_char(xte.source_id_int_1) VCHRID,
to_number(to_char(xal.ACCOUNTING_DATE, 'yyyy')) ACCTYEAR,
to_char(xal.ACCOUNTING_DATE, 'mm') ACCTPERIOD,
xal.ae_line_num ROWFLAG,
decode(xte.entity_code,
'AP_INVOICES',
'2',
'AP_PAYMENTS',
'3',
'RECEIPTS',
'5',
'TRANSACTIONS',
'4') vchrtypecode,
/*(SELECT to_char(aia.voucher_num)
FROM ap_invoices_all aia
WHERE 1 = 1
AND ora_hash(xte.entity_code) || to_char(aia.invoice_id) =
ora_hash(xte.entity_code) || to_char(xte.source_id_int_1)
AND rownum = 1) vchrnum,*/
'PENDING' vchrnum,
to_char(xal.ACCOUNTING_DATE, 'yyyy-mm-dd hh24:mi:ss') createdate,
'Y' postflag,
(DECODE((SELECT goc.attribute2
FROM gj_organization_coa_flex_value t,
gj_organization_coa_flex_type goc
WHERE 1 = 1
AND t.org_coa_acc_code = goc.org_coa_code
AND t.COA_ACC_CODE = gcck.segment3
AND goc.attribute1 IN ('成本类', '损益类')),
'贷方',
0,
'借方',
nvl(xal.accounted_cr * -1, 0) +
nvl(xal.accounted_dr, 0),
nvl(xal.accounted_dr, 0))) DEBIT,
(DECODE((SELECT goc.attribute2
FROM gj_organization_coa_flex_value t,
gj_organization_coa_flex_type goc
WHERE 1 = 1
AND t.org_coa_acc_code = goc.org_coa_code
AND t.COA_ACC_CODE = gcck.segment3
AND goc.attribute1 IN ('成本类', '损益类')),
'借方',
0,
'贷方',
nvl(xal.accounted_dr * -1, 0) +
nvl(xal.accounted_cr, 0),
nvl(xal.accounted_cr, 0))) CREDIT,
(DECODE((SELECT goc.attribute2
FROM gj_organization_coa_flex_value t,
gj_organization_coa_flex_type goc
WHERE 1 = 1
AND t.org_coa_acc_code = goc.org_coa_code
AND t.COA_ACC_CODE = gcck.segment3
AND goc.attribute1 IN ('成本类', '损益类')),
'贷方',
0,
'借方',
nvl(xal.accounted_cr * -1, 0) +
nvl(xal.accounted_dr, 0),
nvl(xal.accounted_dr, 0))) ORGND,
(DECODE((SELECT goc.attribute2
FROM gj_organization_coa_flex_value t,
gj_organization_coa_flex_type goc
WHERE 1 = 1
AND t.org_coa_acc_code = goc.org_coa_code
AND t.COA_ACC_CODE = gcck.segment3
AND goc.attribute1 IN ('成本类', '损益类')),
'借方',
0,
'贷方',
nvl(xal.accounted_dr * -1, 0) +
nvl(xal.accounted_cr, 0),
nvl(xal.accounted_cr, 0))) ORGNC,
0 QTY,
0 PRICE,
decode(xal.CURRENCY_CODE,
'CNY',
1,
nvl(xal.CURRENCY_CONVERSION_RATE, 1)) EXCHRATE,
nvl(nvl(xal.description, xah.DESCRIPTION), '入账') DIGEST,
to_date(xal.ACCOUNTING_DATE, 'yyyy-mm-dd hh24:mi:ss') bizdate,
NULL expiredate,
NULL md_staff,
DECODE(gcck.segment2, '0', NULL, SUBSTR(gcck.segment2, 1, 4)) MD_DEPARTMENT,
NULL md_custsupplier,
'PENDING' MD_CUSTOMER,
--xal.PARTY_TYPE_CODE || pv.segment1 MD_SUPPLIER,
'PENDING' MD_SUPPLIER,
/*DECODE((SELECT ffvs.FLEX_VALUE
FROM fnd_flex_values_vl ffvs, fnd_flex_value_sets ffv
WHERE 1 = 1
AND ffvs.FLEX_VALUE_SET_ID = ffv.FLEX_VALUE_SET_ID
AND ffv.FLEX_VALUE_SET_NAME = 'GJ_INTER_VIM_INDUSTRY'
AND ffvs.FLEX_VALUE = SUBSTR(gcck.segment3, 1, 4)
AND ffvs.ENABLED_FLAG = 'Y'
AND nvl(ffvs.END_DATE_ACTIVE, SYSDATE) >= SYSDATE),
NULL,
NULL,
(SELECT flv.DESCRIPTION
FROM fnd_lookup_values flv
WHERE 1 = 1
AND flv.LOOKUP_TYPE = 'GJ_INTER_LEGAL_TYPE'
AND flv.LANGUAGE = 'ZHS'
AND flv.ENABLED_FLAG = 'Y'
AND nvl(flv.END_DATE_ACTIVE, SYSDATE) >= SYSDATE
AND flv.LOOKUP_CODE = xep.LEGAL_ENTITY_IDENTIFIER)) MD_INDUSTRY,*/
'PENDING' MD_INDUSTRY,
/*(SELECT aid.attribute1
FROM ap_invoices_all aia, ap_invoice_distributions_all aid
WHERE 1 = 1
AND aia.invoice_id = aid.invoice_id
AND ora_hash(xte.entity_code) || to_char(aia.invoice_id) =
ora_hash(xte.entity_code) || to_char(xte.source_id_int_1)
AND rownum = 1) MD_CFITEM,*/
'PENDING' MD_CFITEM,
DECODE((SELECT ffvs.FLEX_VALUE
FROM fnd_flex_values_vl ffvs, fnd_flex_value_sets ffv
WHERE 1 = 1
AND ffvs.FLEX_VALUE_SET_ID = ffv.FLEX_VALUE_SET_ID
AND ffv.FLEX_VALUE_SET_NAME =
'GJ_INTER_VIM_MARBASCLASS'
AND ffvs.FLEX_VALUE = SUBSTR(gcck.segment3, 1, 4)
AND ffvs.ENABLED_FLAG = 'Y'
AND nvl(ffvs.END_DATE_ACTIVE, SYSDATE) >= SYSDATE),
NULL,
NULL,
(SELECT flv.DESCRIPTION
FROM fnd_lookup_values flv
WHERE 1 = 1
AND flv.LOOKUP_TYPE = 'GJ_INTER_MARBASCLASS'
AND flv.LANGUAGE = 'ZHS'
AND flv.ENABLED_FLAG = 'Y'
AND nvl(flv.END_DATE_ACTIVE, SYSDATE) >= SYSDATE
AND flv.LOOKUP_CODE = gcck.segment1)) MD_MARBASCLASS,
NULL md_measurement,
gcck.segment4 MD_BANKACCOUNT,
NULL MD_REASON,
NULL md_project,
NULL md_contract,
NULL md_inde_project,
NULL MD_FUNDPLAN,
NULL MD_FINANCING,
NULL def_01,
NULL def_02,
NULL def_03,
NULL def_04,
NULL def_05,
NULL def_06,
NULL def_07,
NULL def_08,
NULL def_09,
to_date(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') update_time
FROM xla_ae_headers xah,
xla_ae_lines xal,
xla.xla_transaction_entities xte,
gl_code_combinations_kfv gcck,
--
fnd_lookup_values flv
WHERE 1 = 1
AND xah.AE_HEADER_ID = xal.AE_HEADER_ID
AND xte.ledger_id = 2021
AND xte.entity_id = xah.entity_id
AND (nvl(accounted_cr, 0) <> 0 OR nvl(accounted_dr, 0) <> 0 OR
fnd_profile.value('XLA_SHOW_ZERO_AMT_JRNL') = 'Y')
AND xte.application_id IN (200, 222) --应付/应收模块
AND xte.application_id = xah.application_id
AND xte.entity_code IN
('AP_INVOICES', 'AP_PAYMENTS', 'RECEIPTS', 'TRANSACTIONS')
AND xal.code_combination_id = gcck.code_combination_id
--最终过账
AND flv.LANGUAGE = 'ZHS'
AND flv.VIEW_APPLICATION_ID = 602
AND flv.lookup_type = 'XLA_ACCOUNTING_ENTRY_STATUS'
AND flv.meaning = '最终'
AND flv.lookup_code = xah.accounting_entry_status_code
-- 参数
AND xal.ACCOUNTING_DATE >= to_date('2020-01-01', 'yyyy-mm-dd')
-- 同步的ou参数
AND EXISTS
(SELECT 1
FROM fnd_lookup_values flv, hr_operating_units hou
WHERE 1 = 1
AND flv.LOOKUP_TYPE = 'GJ_INTER_COM'
AND flv.LANGUAGE = 'ZHS'
AND nvl(flv.END_DATE_ACTIVE, SYSDATE) >= SYSDATE
AND flv.ENABLED_FLAG = 'Y'
AND hou.short_code = gcck.segment1);

BEGIN
-- 日志开始
g_log_msg := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
l_api_name || ' 程序开始';
log(g_log_msg);

OPEN cur_voucher_lines;
LOOP
FETCH cur_voucher_lines BULK COLLECT
INTO l_rec_line_list LIMIT 1000;

FORALL i IN l_rec_line_list.first .. l_rec_line_list.last
INSERT INTO INF_GL_VOUCHERITEMASS VALUES l_rec_line_list (i);

-- 每1000条提交一次
COMMIT;

EXIT WHEN cur_voucher_lines%NOTFOUND;
END LOOP;
CLOSE cur_voucher_lines;

-- 日志结束
g_log_msg := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
l_api_name || ' 程序开始';
log(g_log_msg);
EXCEPTION
WHEN OTHERS THEN
x_return_status := g_error;
x_msg_data := l_api_name || '->' || SQLERRM ||
dbms_utility.format_error_backtrace;
log(x_msg_data);
END sp_vchrnum_lines;

/**
* @Author : KALOSORA
* @Purpose : 根据已经同步的会计分录行,匹配应付发票头数据
*
* @Param :x_return_status 返回的参数状态
* @Param :x_return_status 返回的消息
* @Version : 1.0
* @Since : 2021/1/19 9:12:35
*/
PROCEDURE sp_ap_invoice_headers(x_return_status OUT NOCOPY VARCHAR2,
x_msg_count OUT NOCOPY NUMBER,
x_msg_data OUT NOCOPY VARCHAR2) IS
l_api_name CONSTANT VARCHAR2(100) := 'sp_ap_invoice_headers';

TYPE l_rec_header IS TABLE OF INF_GL_VOUCHER%ROWTYPE;
l_rec_header_list l_rec_header;

CURSOR cur_invoice_headers IS
SELECT infvhr.id,
'GJ_CN_01' BOOKCODE,
xep.LEGAL_ENTITY_IDENTIFIER UNITCODE,
infvhr.ACCTYEAR,
infvhr.ACCTPERIOD,
infvhr.VCHRTYPECODE,
infvhr.VCHRNUM,
fu.USER_NAME CREATOR,
infvhr.CREATETIME,
infvhr.CREATEDATE,
infvhr.POSTDATE,
NULL POSTOR,
NULL CASHIER,
NULL CASHDATE,
NULL ATTACHMENT,
infvhr.ADJPERIODFLAG,
'0' TEMPFLAG,
'0' OFFSETSTATE,
NULL OFFSETID,
'0' BLUEOFFSETSTATE,
NULL BLUEOFFSETID,
infvhr.MD_SUPPLIER def_01,
infvhr.MD_CFITEM def_02,
NULL def_03,
NULL def_04,
NULL def_05,
NULL def_06,
NULL def_07,
NULL def_08,
NULL def_09,
to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') update_time
FROM (SELECT ora_hash('AP_INVOICES') || aia.invoice_id ID,
aia.org_id,
aia.invoice_id,
aia.CREATED_BY,
to_number(to_char(aia.GL_DATE, 'yyyy')) ACCTYEAR,
to_char(aia.GL_DATE, 'mm') ACCTPERIOD,
(SELECT flv.LOOKUP_CODE
FROM fnd_lookup_values flv
WHERE 1 = 1
AND flv.LOOKUP_TYPE = 'GJ_INTER_MD_VCHRTYPE'
AND flv.LANGUAGE = 'ZHS'
AND flv.ENABLED_FLAG = 'Y'
AND NVL(flv.END_DATE_ACTIVE, SYSDATE) >= SYSDATE
AND flv.MEANING = '应付发票') VCHRTYPECODE,
nvl(to_char(aia.VOUCHER_NUM),
GJ_CONGL_UTIL.get_virtual_voucher) VCHRNUM,
to_char(aia.GL_DATE, 'yyyy-mm-dd hh24:mi:ss') CREATETIME,
to_char(aia.GL_DATE, 'yyyy-mm-dd hh24:mi:ss') CREATEDATE,
to_char(aia.GL_DATE, 'yyyy-mm-dd hh24:mi:ss') POSTDATE,
'0' ADJPERIODFLAG,
'S' || pv.segment1 MD_SUPPLIER,
(SELECT aid.attribute1
FROM ap_invoice_distributions_all aid
WHERE 1 = 1
AND aid.INVOICE_ID = aia.invoice_id
AND rownum = 1) MD_CFITEM
FROM ap_invoices_all aia, po_vendors pv
WHERE 1 = 1
AND pv.vendor_id = aia.vendor_id) infvhr,
hr_operating_units hou,
xle_entity_profiles xep,
fnd_user fu
WHERE 1 = 1
AND xep.LEGAL_ENTITY_ID = hou.default_legal_context_id
AND hou.organization_id = infvhr.org_id
AND fu.user_id = infvhr.created_by
AND EXISTS (SELECT 1
FROM INF_GL_VOUCHERITEMASS igv
WHERE 1 = 1
AND igv.vchrtypecode = infvhr.vchrtypecode
AND igv.vchrtypecode = '2'
AND igv.vchrid = infvhr.id);
BEGIN
-- 日志开始
g_log_msg := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
l_api_name || ' 程序开始';
log(g_log_msg);

OPEN cur_invoice_headers;
LOOP
FETCH cur_invoice_headers BULK COLLECT
INTO l_rec_header_list LIMIT 1000;

FORALL i IN l_rec_header_list.first .. l_rec_header_list.last
INSERT INTO INF_GL_VOUCHER VALUES l_rec_header_list (i);

-- 每1000条提交一次
COMMIT;

EXIT WHEN cur_invoice_headers%NOTFOUND;
END LOOP;
CLOSE cur_invoice_headers;

-- 日志结束
g_log_msg := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
l_api_name || ' 程序开始';
log(g_log_msg);

EXCEPTION
WHEN OTHERS THEN
x_return_status := g_error;
x_msg_data := l_api_name || '->' || SQLERRM ||
dbms_utility.format_error_backtrace;
log(x_msg_data);
END sp_ap_invoice_headers;

/**
* @Author : KALOSORA
* @Purpose : 根据已经同步的会计分录行,匹配应付付款头数据
*
* @Param :x_return_status 返回的参数状态
* @Param :x_return_status 返回的消息
* @Version : 1.0
* @Since : 2021/1/19 9:12:35
*/
PROCEDURE sp_ap_payment_headers(x_return_status OUT NOCOPY VARCHAR2,
x_msg_count OUT NOCOPY NUMBER,
x_msg_data OUT NOCOPY VARCHAR2) IS
l_api_name CONSTANT VARCHAR2(100) := 'sp_ap_payment_headers';

TYPE l_rec_header IS TABLE OF INF_GL_VOUCHER%ROWTYPE;
l_rec_header_list l_rec_header;

CURSOR cur_payment_headers IS
SELECT infvhr.id,
'GJ_CN_01' BOOKCODE,
xep.LEGAL_ENTITY_IDENTIFIER UNITCODE,
infvhr.ACCTYEAR,
infvhr.ACCTPERIOD,
infvhr.VCHRTYPECODE,
infvhr.VCHRNUM,
fu.USER_NAME CREATOR,
infvhr.CREATETIME,
infvhr.CREATEDATE,
infvhr.POSTDATE,
NULL POSTOR,
NULL CASHIER,
NULL CASHDATE,
NULL ATTACHMENT,
infvhr.ADJPERIODFLAG,
'0' TEMPFLAG,
'0' OFFSETSTATE,
NULL OFFSETID,
'0' BLUEOFFSETSTATE,
NULL BLUEOFFSETID,
infvhr.MD_SUPPLIER def_01,
infvhr.MD_CFITEM def_02,
NULL def_03,
NULL def_04,
NULL def_05,
NULL def_06,
NULL def_07,
NULL def_08,
NULL def_09,
to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') update_time
FROM (SELECT ora_hash('AP_PAYMENTS') || aca.check_id ID,
aca.org_id,
aca.check_id,
aca.CREATED_BY,
to_number(to_char(aca.CHECK_DATE, 'yyyy')) ACCTYEAR,
to_char(aca.CHECK_DATE, 'mm') ACCTPERIOD,
(SELECT flv.LOOKUP_CODE
FROM fnd_lookup_values flv
WHERE 1 = 1
AND flv.LOOKUP_TYPE = 'GJ_INTER_MD_VCHRTYPE'
AND flv.LANGUAGE = 'ZHS'
AND flv.ENABLED_FLAG = 'Y'
AND NVL(flv.END_DATE_ACTIVE, SYSDATE) >= SYSDATE
AND flv.MEANING = '应付付款') VCHRTYPECODE,
nvl(to_char(aca.DOC_SEQUENCE_VALUE),
GJ_CONGL_UTIL.get_virtual_voucher) VCHRNUM,
to_char(aca.CHECK_DATE, 'yyyy-mm-dd hh24:mi:ss') CREATETIME,
to_char(aca.CHECK_DATE, 'yyyy-mm-dd hh24:mi:ss') CREATEDATE,
to_char(aca.CHECK_DATE, 'yyyy-mm-dd hh24:mi:ss') POSTDATE,
'0' ADJPERIODFLAG,
'S' || pv.segment1 MD_SUPPLIER,
aca.attribute4 MD_CFITEM
FROM ap_checks_all aca, po_vendors pv
WHERE 1 = 1
AND pv.VENDOR_ID = aca.vendor_id) infvhr,
hr_operating_units hou,
xle_entity_profiles xep,
fnd_user fu
WHERE 1 = 1
AND xep.LEGAL_ENTITY_ID = hou.default_legal_context_id
AND hou.organization_id = infvhr.org_id
AND fu.user_id = infvhr.created_by
AND EXISTS (SELECT 1
FROM INF_GL_VOUCHERITEMASS igv
WHERE 1 = 1
AND igv.vchrtypecode = infvhr.vchrtypecode
AND igv.vchrtypecode = '3'
AND igv.vchrid = infvhr.id);
BEGIN

-- 日志开始
g_log_msg := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
l_api_name || ' 程序开始';
log(g_log_msg);

OPEN cur_payment_headers;
LOOP
FETCH cur_payment_headers BULK COLLECT
INTO l_rec_header_list LIMIT 1000;

FORALL i IN l_rec_header_list.first .. l_rec_header_list.last
INSERT INTO INF_GL_VOUCHER VALUES l_rec_header_list (i);

-- 每1000条提交一次
COMMIT;

EXIT WHEN cur_payment_headers%NOTFOUND;
END LOOP;
CLOSE cur_payment_headers;

-- 日志结束
g_log_msg := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
l_api_name || ' 程序开始';
log(g_log_msg);
EXCEPTION
WHEN OTHERS THEN
x_return_status := g_error;
x_msg_data := l_api_name || '->' || SQLERRM ||
dbms_utility.format_error_backtrace;
log(x_msg_data);
END sp_ap_payment_headers;

/**
* @Author : KALOSORA
* @Purpose : 根据已经同步的会计分录行,匹配应收发票头数据
*
* @Param :x_return_status 返回的参数状态
* @Param :x_return_status 返回的消息
* @Version : 1.0
* @Since : 2021/1/19 9:12:35
*/
PROCEDURE sp_ar_invoice_headers(x_return_status OUT NOCOPY VARCHAR2,
x_msg_count OUT NOCOPY NUMBER,
x_msg_data OUT NOCOPY VARCHAR2) IS
l_api_name CONSTANT VARCHAR2(100) := 'sp_ar_invoice_headers';

TYPE l_rec_header IS TABLE OF INF_GL_VOUCHER%ROWTYPE;
l_rec_header_list l_rec_header;

CURSOR cur_transaction_headers IS
SELECT infvhr.id,
'GJ_CN_01' BOOKCODE,
xep.LEGAL_ENTITY_IDENTIFIER UNITCODE,
infvhr.ACCTYEAR,
infvhr.ACCTPERIOD,
infvhr.VCHRTYPECODE,
infvhr.VCHRNUM,
fu.USER_NAME CREATOR,
infvhr.CREATETIME,
infvhr.CREATEDATE,
infvhr.POSTDATE,
NULL POSTOR,
NULL CASHIER,
NULL CASHDATE,
NULL ATTACHMENT,
infvhr.ADJPERIODFLAG,
'0' TEMPFLAG,
'0' OFFSETSTATE,
NULL OFFSETID,
'0' BLUEOFFSETSTATE,
NULL BLUEOFFSETID,
infvhr.MD_CUSTOMER def_01,
infvhr.MD_CFITEM def_02,
NULL def_03,
NULL def_04,
NULL def_05,
NULL def_06,
NULL def_07,
NULL def_08,
NULL def_09,
to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') update_time
FROM (SELECT ora_hash('TRANSACTIONS') || rct.CUSTOMER_TRX_ID id,
rct.ORG_ID,
rct.CUSTOMER_TRX_ID CUSTOMER_TRX_ID,
rct.CREATED_BY,
to_number(to_char((SELECT rctlgd.GL_DATE
FROM ra_cust_trx_line_gl_dist_all rctlgd
WHERE 1 = 1
AND rctlgd.CUSTOMER_TRX_ID =
rct.CUSTOMER_TRX_ID
AND rctlgd.gl_date IS NOT NULL
AND rownum = 1),
'yyyy')) ACCTYEAR,
to_char((SELECT rctlgd.GL_DATE
FROM ra_cust_trx_line_gl_dist_all rctlgd
WHERE 1 = 1
AND rctlgd.CUSTOMER_TRX_ID =
rct.CUSTOMER_TRX_ID
AND rctlgd.gl_date IS NOT NULL
AND rownum = 1),
'mm') ACCTPERIOD,
(SELECT flv.LOOKUP_CODE
FROM fnd_lookup_values flv
WHERE 1 = 1
AND flv.LOOKUP_TYPE = 'GJ_INTER_MD_VCHRTYPE'
AND flv.LANGUAGE = 'ZHS'
AND flv.ENABLED_FLAG = 'Y'
AND NVL(flv.END_DATE_ACTIVE, SYSDATE) >= SYSDATE
AND flv.MEANING = '应收发票') VCHRTYPECODE,
nvl(to_char(rct.DOC_SEQUENCE_VALUE),
GJ_CONGL_UTIL.get_virtual_voucher) VCHRNUM,
to_char((SELECT rctlgd.GL_DATE
FROM ra_cust_trx_line_gl_dist_all rctlgd
WHERE 1 = 1
AND rctlgd.CUSTOMER_TRX_ID =
rct.CUSTOMER_TRX_ID
AND rctlgd.GL_DATE IS NOT NULL
AND rownum = 1),
'yyyy-mm-dd hh24:mi:ss') CREATETIME,
to_char((SELECT rctlgd.GL_DATE
FROM ra_cust_trx_line_gl_dist_all rctlgd
WHERE 1 = 1
AND rctlgd.CUSTOMER_TRX_ID =
rct.CUSTOMER_TRX_ID
AND rctlgd.GL_DATE IS NOT NULL
AND rownum = 1),
'yyyy-mm-dd hh24:mi:ss') CREATEDATE,
to_char((SELECT rctlgd.GL_DATE
FROM ra_cust_trx_line_gl_dist_all rctlgd
WHERE 1 = 1
AND rctlgd.CUSTOMER_TRX_ID =
rct.CUSTOMER_TRX_ID
AND rctlgd.GL_DATE IS NOT NULL
AND rownum = 1),
'yyyy-mm-dd hh24:mi:ss') POSTDATE,
'0' ADJPERIODFLAG,
'C' || hca.ACCOUNT_NUMBER MD_CUSTOMER,
NULL MD_CFITEM
FROM ra_customer_trx_all rct,
hz_cust_accounts hca,
hz_parties hp
WHERE 1 = 1
AND hca.cust_account_id = rct.BILL_TO_CUSTOMER_ID
AND hca.PARTY_ID = hp.PARTY_ID) infvhr,
hr_operating_units hou,
xle_entity_profiles xep,
fnd_user fu
WHERE 1 = 1
AND xep.LEGAL_ENTITY_ID = hou.default_legal_context_id
AND hou.organization_id = infvhr.org_id
AND fu.user_id = infvhr.created_by
AND EXISTS (SELECT 1
FROM INF_GL_VOUCHERITEMASS igv
WHERE 1 = 1
AND igv.vchrtypecode = infvhr.vchrtypecode
AND igv.vchrtypecode = '4'
AND igv.vchrid = infvhr.id);
BEGIN

-- 日志开始
g_log_msg := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
l_api_name || ' 程序开始';
log(g_log_msg);

OPEN cur_transaction_headers;
LOOP
FETCH cur_transaction_headers BULK COLLECT
INTO l_rec_header_list LIMIT 1000;

FORALL i IN l_rec_header_list.first .. l_rec_header_list.last
INSERT INTO INF_GL_VOUCHER VALUES l_rec_header_list (i);

-- 每1000条提交一次
COMMIT;

EXIT WHEN cur_transaction_headers%NOTFOUND;
END LOOP;
CLOSE cur_transaction_headers;

-- 日志结束
g_log_msg := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
l_api_name || ' 程序开始';
log(g_log_msg);
EXCEPTION
WHEN OTHERS THEN
x_return_status := g_error;
x_msg_data := l_api_name || '->' || SQLERRM ||
dbms_utility.format_error_backtrace;
log(x_msg_data);
END sp_ar_invoice_headers;

/**
* @Author : KALOSORA
* @Purpose : 根据已经同步的会计分录行,匹配应收收款头数据
*
* @Param :x_return_status 返回的参数状态
* @Param :x_return_status 返回的消息
* @Version : 1.0
* @Since : 2021/1/19 9:12:35
*/
PROCEDURE sp_ar_receipt_headers(x_return_status OUT NOCOPY VARCHAR2,
x_msg_count OUT NOCOPY NUMBER,
x_msg_data OUT NOCOPY VARCHAR2) IS
l_api_name CONSTANT VARCHAR2(100) := 'sp_ar_receipt_headers';

TYPE l_rec_header IS TABLE OF INF_GL_VOUCHER%ROWTYPE;
l_rec_header_list l_rec_header;

CURSOR cur_receipt_headers IS
SELECT infvhr.id,
'GJ_CN_01' BOOKCODE,
xep.LEGAL_ENTITY_IDENTIFIER UNITCODE,
infvhr.ACCTYEAR,
infvhr.ACCTPERIOD,
infvhr.VCHRTYPECODE,
infvhr.VCHRNUM,
fu.USER_NAME CREATOR,
infvhr.CREATETIME,
infvhr.CREATEDATE,
infvhr.POSTDATE,
NULL POSTOR,
NULL CASHIER,
NULL CASHDATE,
NULL ATTACHMENT,
infvhr.ADJPERIODFLAG,
'0' TEMPFLAG,
'0' OFFSETSTATE,
NULL OFFSETID,
'0' BLUEOFFSETSTATE,
NULL BLUEOFFSETID,
infvhr.MD_CUSTOMER def_01,
infvhr.MD_CFITEM def_02,
NULL def_03,
NULL def_04,
NULL def_05,
NULL def_06,
NULL def_07,
NULL def_08,
NULL def_09,
to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') update_time
FROM (SELECT ora_hash('RECEIPTS') || acr.CASH_RECEIPT_ID id,
acr.ORG_ID,
acr.CASH_RECEIPT_ID,
acr.CREATED_BY,
to_number(to_char((SELECT acrh.GL_DATE
FROM AR_CASH_RECEIPT_HISTORY_ALL acrh
WHERE acrh.CASH_RECEIPT_ID =
acr.CASH_RECEIPT_ID
AND acrh.org_id = acr.org_id
AND acrh.FIRST_POSTED_RECORD_FLAG(+) = 'Y'),
'yyyy')) ACCTYEAR,
to_char((SELECT acrh.GL_DATE
FROM AR_CASH_RECEIPT_HISTORY_ALL acrh
WHERE acrh.CASH_RECEIPT_ID =
acr.CASH_RECEIPT_ID
AND acrh.org_id = acr.org_id
AND acrh.FIRST_POSTED_RECORD_FLAG(+) = 'Y'),
'mm') ACCTPERIOD,
(SELECT flv.LOOKUP_CODE
FROM fnd_lookup_values flv
WHERE 1 = 1
AND flv.LOOKUP_TYPE = 'GJ_INTER_MD_VCHRTYPE'
AND flv.LANGUAGE = 'ZHS'
AND flv.ENABLED_FLAG = 'Y'
AND NVL(flv.END_DATE_ACTIVE, SYSDATE) >= SYSDATE
AND flv.MEANING = '应收收款') VCHRTYPECODE,
nvl(to_char(acr.DOC_SEQUENCE_VALUE),
GJ_CONGL_UTIL.get_virtual_voucher) VCHRNUM,
to_char((SELECT acrh.GL_DATE
FROM AR_CASH_RECEIPT_HISTORY_ALL acrh
WHERE acrh.CASH_RECEIPT_ID =
acr.CASH_RECEIPT_ID
AND acrh.org_id = acr.org_id
AND acrh.FIRST_POSTED_RECORD_FLAG(+) = 'Y'),
'yyyy-mm-dd hh24:mi:ss') CREATETIME,
to_char((SELECT acrh.GL_DATE
FROM AR_CASH_RECEIPT_HISTORY_ALL acrh
WHERE acrh.CASH_RECEIPT_ID =
acr.CASH_RECEIPT_ID
AND acrh.org_id = acr.org_id
AND acrh.FIRST_POSTED_RECORD_FLAG(+) = 'Y'),
'yyyy-mm-dd hh24:mi:ss') CREATEDATE,
to_char((SELECT acrh.GL_DATE
FROM AR_CASH_RECEIPT_HISTORY_ALL acrh
WHERE acrh.CASH_RECEIPT_ID =
acr.CASH_RECEIPT_ID
AND acrh.org_id = acr.org_id
AND acrh.FIRST_POSTED_RECORD_FLAG(+) = 'Y'),
'yyyy-mm-dd hh24:mi:ss') POSTDATE,
'0' ADJPERIODFLAG,
'C' || hca.ACCOUNT_NUMBER MD_CUSTOMER,
'1111' MD_CFITEM
FROM ar_cash_receipts_all acr,
hz_cust_accounts hca,
hz_parties hp
WHERE 1 = 1
AND acr.PAY_FROM_CUSTOMER = hca.cust_account_id
AND hca.PARTY_ID = hp.PARTY_ID) infvhr,
hr_operating_units hou,
xle_entity_profiles xep,
fnd_user fu
WHERE 1 = 1
AND xep.LEGAL_ENTITY_ID = hou.default_legal_context_id
AND hou.organization_id = infvhr.org_id
AND fu.user_id = infvhr.created_by
AND EXISTS (SELECT 1
FROM INF_GL_VOUCHERITEMASS igv
WHERE 1 = 1
AND igv.vchrtypecode = infvhr.vchrtypecode
AND igv.vchrtypecode = '5'
AND igv.vchrid = infvhr.id);
BEGIN
-- 日志开始
g_log_msg := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
l_api_name || ' 程序开始';
log(g_log_msg);

OPEN cur_receipt_headers;
LOOP
FETCH cur_receipt_headers BULK COLLECT
INTO l_rec_header_list LIMIT 1000;

FORALL i IN l_rec_header_list.first .. l_rec_header_list.last
INSERT INTO INF_GL_VOUCHER VALUES l_rec_header_list (i);

-- 每1000条提交一次
COMMIT;

EXIT WHEN cur_receipt_headers%NOTFOUND;
END LOOP;
CLOSE cur_receipt_headers;

-- 日志结束
g_log_msg := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
l_api_name || ' 程序开始';
log(g_log_msg);
EXCEPTION
WHEN OTHERS THEN
x_return_status := g_error;
x_msg_data := l_api_name || '->' || SQLERRM ||
dbms_utility.format_error_backtrace;
log(x_msg_data);
END sp_ar_receipt_headers;

/**
* @Author : KALOSORA
* @Purpose : 整理本地数据集,刷新 PENDING 的字段
*
* @Param :x_return_status 返回的参数状态
* @Param :x_return_status 返回的消息
* @Version : 1.0
* @Since : 2021/1/19 9:12:35
*/
PROCEDURE update_local_data(x_return_status OUT NOCOPY VARCHAR2,
x_msg_count OUT NOCOPY NUMBER,
x_msg_data OUT NOCOPY VARCHAR2) IS
l_api_name CONSTANT VARCHAR2(100) := 'update_local_data';

-- 批量更新的数据结构
TYPE record_update_type IS RECORD(
id inf_gl_voucheritemass.id%TYPE,
vchrid inf_gl_voucheritemass.vchrid%TYPE,
unitcode inf_gl_voucheritemass.unitcode%TYPE,
vchrnum inf_gl_voucheritemass.vchrnum%TYPE,
MD_CUSTOMER inf_gl_voucheritemass.MD_CUSTOMER%TYPE,
MD_CFITEM inf_gl_voucheritemass.MD_CFITEM%TYPE,
MD_INDUSTRY inf_gl_voucheritemass.md_industry%TYPE);
TYPE record_update_tbl IS TABLE OF record_update_type;
record_update record_update_tbl;

-- 应付数据集
CURSOR cur_ap_data IS
SELECT igvm.id,
igvm.vchrid,
igv.unitcode,
igv.vchrnum,
igv.def_01 MD_CUSTOMER,
igv.def_02 MD_CFITEM,
DECODE((SELECT ffvs.FLEX_VALUE
FROM fnd_flex_values_vl ffvs, fnd_flex_value_sets ffv
WHERE 1 = 1
AND ffvs.FLEX_VALUE_SET_ID = ffv.FLEX_VALUE_SET_ID
AND ffv.FLEX_VALUE_SET_NAME =
'GJ_INTER_VIM_INDUSTRY'
AND ffvs.FLEX_VALUE = igvm.SUBJECTCODE
AND ffvs.ENABLED_FLAG = 'Y'
AND nvl(ffvs.END_DATE_ACTIVE, SYSDATE) >= SYSDATE),
NULL,
NULL,
(SELECT flv.DESCRIPTION
FROM fnd_lookup_values flv
WHERE 1 = 1
AND flv.LOOKUP_TYPE = 'GJ_INTER_LEGAL_TYPE'
AND flv.LANGUAGE = 'ZHS'
AND flv.ENABLED_FLAG = 'Y'
AND nvl(flv.END_DATE_ACTIVE, SYSDATE) >= SYSDATE
AND flv.LOOKUP_CODE = igv.unitcode)) MD_INDUSTRY
FROM inf_gl_voucher igv, inf_gl_voucheritemass igvm
WHERE 1 = 1
AND igv.id = igvm.vchrid
AND igv.vchrtypecode IN ('3', '2')
AND igvm.vchrnum = 'PENDING';

-- 应收数据集
CURSOR cur_ar_data IS
SELECT igvm.id,
igvm.vchrid,
igv.unitcode,
igv.vchrnum,
igv.def_01 MD_CUSTOMER,
igv.def_02 MD_CFITEM,
DECODE((SELECT ffvs.FLEX_VALUE
FROM fnd_flex_values_vl ffvs, fnd_flex_value_sets ffv
WHERE 1 = 1
AND ffvs.FLEX_VALUE_SET_ID = ffv.FLEX_VALUE_SET_ID
AND ffv.FLEX_VALUE_SET_NAME =
'GJ_INTER_VIM_INDUSTRY'
AND ffvs.FLEX_VALUE = igvm.SUBJECTCODE
AND ffvs.ENABLED_FLAG = 'Y'
AND nvl(ffvs.END_DATE_ACTIVE, SYSDATE) >= SYSDATE),
NULL,
NULL,
(SELECT flv.DESCRIPTION
FROM fnd_lookup_values flv
WHERE 1 = 1
AND flv.LOOKUP_TYPE = 'GJ_INTER_LEGAL_TYPE'
AND flv.LANGUAGE = 'ZHS'
AND flv.ENABLED_FLAG = 'Y'
AND nvl(flv.END_DATE_ACTIVE, SYSDATE) >= SYSDATE
AND flv.LOOKUP_CODE = igv.unitcode)) MD_INDUSTRY
FROM inf_gl_voucher igv, inf_gl_voucheritemass igvm
WHERE 1 = 1
AND igv.id = igvm.vchrid
AND igv.vchrtypecode IN ('4', '5')
AND igvm.vchrnum = 'PENDING';

BEGIN
-- 日志开始
g_log_msg := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
l_api_name || ' 程序开始';
log(g_log_msg);

-- 更新应付数据集
OPEN cur_ap_data;
LOOP
FETCH cur_ap_data BULK COLLECT
INTO record_update LIMIT 1000;

FORALL i IN 1 .. record_update.count
UPDATE inf_gl_voucheritemass
SET MD_CUSTOMER = NULL,
MD_supplier = record_update(i).MD_CUSTOMER,
MD_CFITEM = record_update(i).MD_CFITEM,
MD_INDUSTRY = record_update(i).MD_INDUSTRY,
unitcode = record_update(i).unitcode,
vchrnum = record_update(i).vchrnum
WHERE 1 = 1
AND id = record_update(i).id
AND vchrid = record_update(i).vchrid;

COMMIT;

EXIT WHEN cur_ap_data%NOTFOUND;
END LOOP;
CLOSE cur_ap_data;

-- 清空数据集
record_update.delete;

-- 更新应收数据集
OPEN cur_ar_data;
LOOP
FETCH cur_ar_data BULK COLLECT
INTO record_update LIMIT 1000;

FORALL i IN 1 .. record_update.count
UPDATE inf_gl_voucheritemass
SET MD_supplier = NULL,
MD_CUSTOMER = record_update(i).MD_CUSTOMER,
MD_CFITEM = record_update(i).MD_CFITEM,
MD_INDUSTRY = record_update(i).MD_INDUSTRY,
unitcode = record_update(i).unitcode,
vchrnum = record_update(i).vchrnum
WHERE 1 = 1
AND id = record_update(i).id
AND vchrid = record_update(i).vchrid;

COMMIT;

EXIT WHEN cur_ar_data%NOTFOUND;
END LOOP;
CLOSE cur_ar_data;

-- 日志结束
g_log_msg := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
l_api_name || ' 程序开始';
log(g_log_msg);

EXCEPTION
WHEN OTHERS THEN
x_return_status := g_error;
x_msg_data := l_api_name || '->' || SQLERRM ||
dbms_utility.format_error_backtrace;
log(x_msg_data);
END update_local_data;

/**
* @Author : KALOSORA
* @Purpose : 将本地整理好的数据集插入到中间库
*
* @Param :x_return_status 返回的参数状态
* @Param :x_return_status 返回的消息
* @Version : 1.0
* @Since : 2021/1/19 9:12:35
*/
PROCEDURE sp_remote_data(x_return_status OUT NOCOPY VARCHAR2,
x_msg_count OUT NOCOPY NUMBER,
x_msg_data OUT NOCOPY VARCHAR2) IS
l_api_name CONSTANT VARCHAR2(100) := 'sp_remote_data';

BEGIN
-- 日志开始
g_log_msg := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
l_api_name || ' 程序开始';
log(g_log_msg);

-- 插入到凭证头表
INSERT INTO INF_GL_VOUCHER@gj_congl_middb_prod
SELECT * FROM INF_GL_VOUCHER;

-- 插入到凭证行表
INSERT INTO INF_GL_VOUCHERITEMASS@gj_congl_middb_prod
SELECT * FROM INF_GL_VOUCHERITEMASS;

-- 日志结束
g_log_msg := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
l_api_name || ' 程序开始';
log(g_log_msg);

EXCEPTION
WHEN OTHERS THEN
x_return_status := g_error;
x_msg_data := l_api_name || '->' || SQLERRM ||
dbms_utility.format_error_backtrace;
log(x_msg_data);
END sp_remote_data;

/**
* @Author : KALOSORA
* @Purpose : 全量同步,清空本地数据集和中间库
*
* @Param :x_return_status 返回的参数状态
* @Param :x_return_status 返回的消息
* @Version : 1.0
* @Since : 2021/1/19 9:12:35
*/
PROCEDURE sp_sync_all(x_return_status OUT NOCOPY VARCHAR2,
x_msg_count OUT NOCOPY NUMBER,
x_msg_data OUT NOCOPY VARCHAR2) IS
l_api_name CONSTANT VARCHAR2(100) := 'sp_remote_data';

BEGIN
-- 日志开始
g_log_msg := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
l_api_name || ' 程序开始';
log(g_log_msg);

-- 清空本地数据集
DELETE FROM INF_GL_VOUCHER;
DELETE FROM INF_GL_VOUCHERITEMASS;
COMMIT;

-- 清空中间库
DELETE FROM INF_GL_VOUCHER@gj_congl_middb_prod;
DELETE FROM INF_GL_VOUCHERITEMASS@gj_congl_middb_prod;
COMMIT;

-- 日志结束
g_log_msg := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
l_api_name || ' 程序结束';
log(g_log_msg);

EXCEPTION
WHEN OTHERS THEN
x_return_status := g_error;
x_msg_data := l_api_name || '->' || SQLERRM ||
dbms_utility.format_error_backtrace;
log(x_msg_data);
END sp_sync_all;

/*==================================================
Procedure Name :
raise_exception
Description:
This procedure perform to 处理请求
Argument:
x_return_status : Input Status : E(Error)/U(Unexception)/S(Success)
History:
1.00 2020-09-02 LIYIHUI Creation
==================================================*/
PROCEDURE process_request(p_init_msg_list IN VARCHAR2 DEFAULT fnd_api.g_false,
p_commit IN VARCHAR2 DEFAULT fnd_api.g_false,
x_return_status OUT NOCOPY VARCHAR2,
x_msg_count OUT NOCOPY NUMBER,
x_msg_data OUT NOCOPY VARCHAR2) IS
l_api_name CONSTANT VARCHAR2(30) := 'process_request';
l_savepoint_name CONSTANT VARCHAR2(30) := 'sp_process_request';

BEGIN
x_return_status := cux_api.start_activity(p_pkg_name => g_pkg_name,
p_api_name => l_api_name,
p_savepoint_name => l_savepoint_name,
p_init_msg_list => p_init_msg_list);
raise_exception(x_return_status => x_return_status);

-------------API body start

-- 0.全量同步
sp_sync_all(x_return_status => x_return_status,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);
raise_exception(x_return_status);

-- 1.凭证分录同步
sp_vchrnum_lines(x_return_status => x_return_status,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);
raise_exception(x_return_status);

-- 2.应付发票凭证头同步
sp_ap_invoice_headers(x_return_status => x_return_status,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);
raise_exception(x_return_status);

-- 3.应付付款凭证头同步
sp_ap_payment_headers(x_return_status => x_return_status,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);
raise_exception(x_return_status);

-- 4.应收发票凭证头同步
sp_ar_invoice_headers(x_return_status => x_return_status,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);
raise_exception(x_return_status);

-- 5.应收收款凭证头同步
sp_ar_receipt_headers(x_return_status => x_return_status,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);
raise_exception(x_return_status);

-- 6.更新本地数据集
update_local_data(x_return_status => x_return_status,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);
raise_exception(x_return_status);

-- 7.期初凭证同步
sp_nc_voucher(x_return_status => x_return_status,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);
raise_exception(x_return_status);

-- 8.插入数据到中间库
sp_remote_data(x_return_status => x_return_status,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);
raise_exception(x_return_status);

-------------API body end

x_return_status := cux_api.end_activity(p_pkg_name => g_pkg_name,
p_api_name => l_api_name,
p_commit => p_commit,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);
EXCEPTION
WHEN fnd_api.g_exc_error THEN
x_return_status := cux_api.handle_exceptions(p_pkg_name => g_pkg_name,
p_api_name => l_api_name,
p_savepoint_name => l_savepoint_name,
p_exc_name => cux_api.g_exc_name_error,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);
WHEN fnd_api.g_exc_unexpected_error THEN
x_return_status := cux_api.handle_exceptions(p_pkg_name => g_pkg_name,
p_api_name => l_api_name,
p_savepoint_name => l_savepoint_name,
p_exc_name => cux_api.g_exc_name_unexp,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);
WHEN OTHERS THEN
x_return_status := cux_api.handle_exceptions(p_pkg_name => g_pkg_name,
p_api_name => l_api_name,
p_savepoint_name => l_savepoint_name,
p_exc_name => cux_api.g_exc_name_others,
x_msg_count => x_msg_count,
x_msg_data => x_msg_data);

END process_request;

/*==================================================
Procedure Name :
main
Description:
This procedure perform to 报表主函数
Argument:
errbuf : concurrent return message buffer
retcode : concurrent return status code
0 success / 1 warning / 2 error
p_book_id : 账簿ID
p_date_from : 日期从
p_date_to : 至
p_item_from : 研发项目从
p_item_to : 至

History:
1.00 2020-09-02 LIYIHUI Creation
==================================================*/
PROCEDURE main(errbuf OUT VARCHAR2, retcode OUT VARCHAR2) IS
l_return_status VARCHAR2(1);
l_msg_count NUMBER;
l_msg_data VARCHAR2(4000);

BEGIN

--output header
errbuf := '0';
retcode := NULL;
cux_conc_utl.log_header;

-- 处理请求
process_request(p_init_msg_list => fnd_api.g_true,
p_commit => fnd_api.g_false,
x_return_status => l_return_status,
x_msg_count => l_msg_count,
x_msg_data => l_msg_data);
raise_exception(l_return_status);

--output foot
cux_conc_utl.log_footer;

EXCEPTION
WHEN fnd_api.g_exc_error THEN
cux_conc_utl.log_message_list;
retcode := '1';
fnd_msg_pub.count_and_get(p_encoded => fnd_api.g_false,
p_count => l_msg_count,
p_data => l_msg_data);
IF l_msg_count > 1 THEN
l_msg_data := fnd_msg_pub.get_detail(p_msg_index => fnd_msg_pub.g_first,
p_encoded => fnd_api.g_false);
END IF;
errbuf := l_msg_data;
WHEN fnd_api.g_exc_unexpected_error THEN
cux_conc_utl.log_message_list;
retcode := '2';
fnd_msg_pub.count_and_get(p_encoded => fnd_api.g_false,
p_count => l_msg_count,
p_data => l_msg_data);
IF l_msg_count > 1 THEN
l_msg_data := fnd_msg_pub.get_detail(p_msg_index => fnd_msg_pub.g_first,
p_encoded => fnd_api.g_false);
END IF;
errbuf := l_msg_data;
WHEN OTHERS THEN
fnd_msg_pub.add_exc_msg(p_pkg_name => g_pkg_name,
p_procedure_name => 'MAIN',
p_error_text => substrb(SQLERRM, 1, 240));
cux_conc_utl.log_message_list;
retcode := '2';
errbuf := SQLERRM;
END main;
END gj_congl_voucher_sync_v2;

异常处理

查看异常

1.查询表

SELECT * FROM user_errors;

image.png

2.SQL * Plus 环境中

show errors;

查看警告信息

PL / SQL 警告的消息代码的格式为 PLW- nnnnn

警告信息等级

image.png

查询警告信息等级

image.png

可以通过 alter system 修改服务器参数文件

image.png

可以通过 alter session 修改当前会话

image.png

修改异常信息

启用部分

Alter session set plsql_warnings=’enable:performance’, ‘enable:severe’;

image.png

启用全部

Alter session set plsql_warnings=’enable:all’;

针对过程/函数启用编译警告

alter package gj_bc_vld_data_utl compile plsql_warnings = ‘enable:all’;

image.png

编辑警告

image.png

异常处理准则

  1. 尽可能编写命名异常的异常处理程序,而不要使用 OTHERS 异常处理程序。
  2. 对于每个异常处理程序,请仔细决定是让它提交事务,回滚它还是让它继续。
  3. 每个 PL / SQL 程序的顶层都包含一个异常处理程序,避免未处理的异常。并且在子程序中显示抛出异常RAISE 或在 RAISE_APPLICATION_ERROR

异常实例

1.自定义异常实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CREATE PROCEDURE account_status (
due_date DATE,
today DATE
) AUTHID DEFINER
IS
past_due EXCEPTION; -- declare exception
BEGIN
IF due_date < today THEN
RAISE past_due; -- explicitly raise exception
END IF;
EXCEPTION
WHEN past_due THEN -- handle exception
DBMS_OUTPUT.PUT_LINE ('Account past due.');
END;
/

BEGIN
account_status (TO_DATE('01-JUL-2010', 'DD-MON-YYYY'),
TO_DATE('09-JUL-2010', 'DD-MON-YYYY'));
END;
/

2.使用RAISE_APPLICATION_ERROR自定义异常实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CREATE PROCEDURE account_status (
due_date DATE,
today DATE
) AUTHID DEFINER
IS
BEGIN
IF due_date < today THEN -- explicitly raise exception
RAISE_APPLICATION_ERROR(-20000, 'Account past due.');
END IF;
END;
/

DECLARE
past_due EXCEPTION; -- declare exception
PRAGMA EXCEPTION_INIT (past_due, -20000); -- assign error code to exception
BEGIN
account_status (TO_DATE('01-JUL-2010', 'DD-MON-YYYY'),
TO_DATE('09-JUL-2010', 'DD-MON-YYYY')); -- invoke procedure

EXCEPTION
WHEN past_due THEN -- handle exception
DBMS_OUTPUT.PUT_LINE(TO_CHAR(SQLERRM(-20000)));
END;
/

实验:数据同步接口为例

package定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
CREATE OR REPLACE PACKAGE gj_inter_company_sync IS

/**
* @Author : KALOSORA
* @Purpose : 输出运行日志
* @Param : p_api_name 函数/过程名称
* @Param : p_api_name 日志等级 info/debug/error
* @Param : p_log_status 日志状态 start/end
* @Param : p_message 日志消息
* @Version : 1.0
* @Since : 2021/1/19 9:12:35
*/
PROCEDURE log(p_api_name IN VARCHAR2,
p_log_level IN VARCHAR2,
p_log_status IN VARCHAR2,
p_message IN VARCHAR2);

/**
* @Author : KALOSORA
* @Purpose : 自动根据快码GJ_INTER_COMPANY更新到客商弹性域中
* @Param :x_return_status 返回的参数状态
* @Param :x_return_status 返回的消息
* @Version : 1.0
* @Since : 2021/1/19 9:12:35
*/
PROCEDURE process_flex_sync(x_return_status OUT NOCOPY VARCHAR2,
x_return_msg OUT NOCOPY VARCHAR2);

/**
* @Author : KALOSORA
* @Purpose : 程序入口
* @Param :x_return_status 返回的参数状态
* @Param :x_return_status 返回的消息
* @Version : 1.0
* @Since : 2021/1/19 9:12:35
*/
PROCEDURE main(x_return_status OUT NOCOPY VARCHAR2,
x_return_msg OUT NOCOPY VARCHAR2);

END gj_inter_company_sync;

package内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
CREATE OR REPLACE PACKAGE BODY gj_inter_company_sync IS

-- ==========================================================
-- 日志参数
g_conc_request_id NUMBER := fnd_global.conc_request_id;
g_log_info VARCHAR2(6) := 'INFO';
g_log_error VARCHAR2(6) := 'ERROR';
g_log_start VARCHAR2(6) := 'START';
g_log_end VARCHAR2(6) := 'END';

-- 自定义应用异常
APP_UNKNOWN_EXCEPTION EXCEPTION;
PRAGMA EXCEPTION_INIT(APP_UNKNOWN_EXCEPTION, -20000);
-- ==========================================================

PROCEDURE log(p_api_name IN VARCHAR2,
p_log_level IN VARCHAR2,
p_log_status IN VARCHAR2,
p_message IN VARCHAR2) IS

BEGIN
IF g_conc_request_id > 0 THEN
fnd_file.PUT_LINE(fnd_file.LOG,
'[' || p_api_name || '] ' || p_log_level || ' ' ||
to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
p_log_status || ' - ' || p_message);
ELSE
dbms_output.put_line('[' || p_api_name || '] ' || p_log_level || ' ' ||
to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss') || ' ' ||
p_log_status || ' - ' || p_message);
END IF;
END;

PROCEDURE process_flex_sync(x_return_status OUT NOCOPY VARCHAR2,
x_return_msg OUT NOCOPY VARCHAR2) IS
l_api_name VARCHAR2(30) := upper('process_flex_sync');

CURSOR cur_vendor IS
SELECT flv.LOOKUP_CODE, company.id, company.inter_company
FROM fnd_lookup_values flv,
(SELECT pv.vendor_id ID,
pv.vendor_name company_name,
pv.attribute2 inter_company
FROM po_vendors pv
WHERE 1 = 1
AND pv.attribute2 IS NULL) company
WHERE 1 = 1
AND company.company_name = flv.DESCRIPTION
AND flv.LOOKUP_TYPE = 'GJ_INTER_COMPANY'
AND flv.LANGUAGE = 'ZHS';

CURSOR cur_customer IS
SELECT flv.LOOKUP_CODE, company.id, company.inter_company
FROM fnd_lookup_values flv,
(SELECT hca.CUST_ACCOUNT_ID ID,
hp.party_name company_name,
hca.ATTRIBUTE5 inter_company
FROM hz_parties hp, hz_cust_accounts hca
WHERE 1 = 1
AND hp.PARTY_ID = hca.PARTY_ID
AND hca.ATTRIBUTE5 IS NULL) company
WHERE 1 = 1
AND company.company_name = flv.DESCRIPTION
AND flv.LOOKUP_TYPE = 'GJ_INTER_COMPANY'
AND flv.LANGUAGE = 'ZHS';

BEGIN

log(l_api_name, g_log_info, g_log_start, NULL);

-- 1. 更新供应商弹性域
FOR rec_vendor IN cur_vendor LOOP
UPDATE po_vendors
SET attribute2 = rec_vendor.LOOKUP_CODE
WHERE 1 = 1
AND vendor_id = rec_vendor.id;
END LOOP;

-- 2. 更新客户弹性域
FOR rec_customer IN cur_customer LOOP
UPDATE hz_cust_accounts
SET attribute5 = rec_customer.LOOKUP_CODE
WHERE 1 = 1
AND CUST_ACCOUNT_ID = rec_customer.id;
END LOOP;

log(l_api_name, g_log_info, g_log_end, NULL);

EXCEPTION
WHEN OTHERS THEN
x_return_status := SQLCODE;
x_return_msg := SQLERRM || dbms_utility.format_error_backtrace;
log(l_api_name, g_log_error, g_log_end, x_return_msg);
RAISE APP_UNKNOWN_EXCEPTION;
END;

PROCEDURE main(x_return_status OUT NOCOPY VARCHAR2,
x_return_msg OUT NOCOPY VARCHAR2) IS
l_return_status VARCHAR2(10);
l_return_msg VARCHAR2(4000);
BEGIN
-- 日志开始
cux_conc_utl.log_header;

process_flex_sync(x_return_status => l_return_status,
x_return_msg => l_return_msg);

-- 日志结束
cux_conc_utl.log_footer;
END;

END gj_inter_company_sync;

4.异常事务处理

使用 SAVEPOINT start_transaction; 记录保存点

使用 rollback to [savepoint_name] 回滚保存点

【注意】在使用savepoint期间不能主动 commit ,否则保存点会消失

条件编译

可以根据不同的判断条件,编译/执行不同的程序

语法 $plsql标识符。注意$和标识符之间不能有空格

实例:输出plsql环境参数

1
2
3
4
5
6
7
8
9
BEGIN
DBMS_OUTPUT.PUT_LINE('$$PLSCOPE_SETTINGS = ' || $$PLSCOPE_SETTINGS);
DBMS_OUTPUT.PUT_LINE('$$PLSQL_CCFLAGS = ' || $$PLSQL_CCFLAGS);
DBMS_OUTPUT.PUT_LINE('$$PLSQL_CODE_TYPE = ' || $$PLSQL_CODE_TYPE);
DBMS_OUTPUT.PUT_LINE('$$PLSQL_OPTIMIZE_LEVEL = ' || $$PLSQL_OPTIMIZE_LEVEL);
DBMS_OUTPUT.PUT_LINE('$$PLSQL_WARNINGS = ' || $$PLSQL_WARNINGS);
DBMS_OUTPUT.PUT_LINE('$$NLS_LENGTH_SEMANTICS = ' || $$NLS_LENGTH_SEMANTICS);
END;
/

实例2:根据数据库版本执行不同的指令

1
2
3
4
5
6
7
8
9
10
11
12
13
BEGIN
$IF DBMS_DB_VERSION.VER_LE_10_1 $THEN -- selection directive begins
$ERROR 'unsupported database release' $END -- error directive
$ELSE
DBMS_OUTPUT.PUT_LINE (
'Release ' || DBMS_DB_VERSION.VERSION || '.' ||
DBMS_DB_VERSION.RELEASE || ' is supported.'
);
-- This COMMIT syntax is newly supported in 10.2:
COMMIT WRITE IMMEDIATE NOWAIT;
$END -- selection directive ends
END;
/

其他的环境变量

可以通过 $$[参数名称] 代替在sql命令窗口中的 show parameter [参数名称],以达到在plsql开发工具中查看服务器参数的效果

  • $$PLSQL_LINE:获取当前package/function的行号
  • $$PLSQL_UNIT:获取当前程序名称
  • $$PLSQL_UNIT_OWNER:获取当前程序owner
  • $$PLSQL_UNIT_TYPE:获取当前程序类型

正则表达式

用到的时候再来总结

https://docs.oracle.com/en/database/oracle/oracle-database/12.2/adfns/regexp.html#GUID-1935FD80-A3CD-413F-BD2E-BBEFE64000B2

数据库锁

同上

闪回技术

Oracle Flashback

实例

查询实例:

1
2
3
4
SELECT * FROM employees
AS OF TIMESTAMP
TO_TIMESTAMP('2004-04-04 09:30:00', 'YYYY-MM-DD HH:MI:SS')
WHERE last_name = 'Chung';

数据恢复实例

1
2
3
4
5
6
INSERT INTO employees (
SELECT * FROM employees
AS OF TIMESTAMP
TO_TIMESTAMP('2004-04-04 09:30:00', 'YYYY-MM-DD HH:MI:SS')
WHERE last_name = 'Chung'
);

闪回事务

https://docs.oracle.com/en/database/oracle/oracle-database/12.2/adfns/flashback.html#GUID-03D1CAAE-D940-444A-8771-B1BC636D105D

消息队列

Oracle AQ

附录

用于管理PLSQL的表

image.png

编程规范

正确地使用数据类型

尽可能精确地指定数据类型

  • 整数时指定NUMBER(n),而不要使用NUMBER。
  • VARCHAR2(n)的精度尽可能准确。

官方参考手册

1.SQL语言参考手册,即SQL Language Reference

https://docs.oracle.com/en/database/oracle/oracle-database/12.2/lnpls/dynamic-sql.html#GUID-7E2F596F-9CA3-4DC8-8333-0C117962DB73

0%