1. 程式人生 > >Oracle PL/SQL 遊標中的更新和刪除

Oracle PL/SQL 遊標中的更新和刪除

 

 遊標中的更新和刪除

  在PL/SQL中依然可以使用UPDATE和DELETE語句更新或刪除資料行。顯式遊標只有在需要獲得多行資料的情
況下使用。PL/SQL提供了僅僅使  用遊標就可以執行刪除或更新記錄的方法。
  UPDATE或DELETE語句中的WHERE CURRENT OF子串專門處理要執行UPDATE或DELETE操作的表中取出的最近的
資料。要使用這個方法,在宣告遊標  時必須使用FOR UPDATE子串,當對話使用FOR UPDATE子串開啟一個遊標時,所有返回集中的資料行都將處於行級(ROW-LEVEL)獨佔式鎖定,其
  他物件只能查詢這些資料行,不能進行UPDATE、DELETE或SELECT...FOR            UPDATE操作。

語法:

  FOR UPDATE [OF [schema.]table.column[,[schema.]table.column]..
  [nowait]

  在多表查詢中,使用OF子句來鎖定特定的表,如果忽略了OF子句,那麼所有表中選擇的資料行都將被鎖定。
如果這些資料行已經被其他會話鎖定,那麼正常情況下ORACLE將等待,直到資料行解鎖。

  在UPDATE和DELETE中使用WHERE CURRENT OF子串的語法如下:

  WHERE{CURRENT OF cursor_name|search_condition}

  例:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

DELCARE

CURSOR c1 IS SELECT empno,salary

FROM emp

WHERE comm IS NULL

FOR UPDATE OF comm;

v_comm NUMBER(10,2);

BEGIN

FOR r1 IN c1 LOOP

IF r1.salary<500 THEN

v_comm:=r1.salary*0.25;

ELSEIF r1.salary<1000 THEN

v_comm:=r1.salary*0.20;

ELSEIF r1.salary<3000

THEN

v_comm:=r1.salary*0.15;

ELSE

v_comm:=r1.salary*0.12;

END IF;

UPDATE emp;

SET comm=v_comm

WHERE CURRENT OF c1l;

END LOOP;

END

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

-宣告遊標

--宗地表的調查日期LANDINFO_RESEARCHDATE

--複製到流程表的權屬調查時間FLOW_REASEARCHTIME

DECLARE

cursor cur_sel_all is select LANDINFO_RESEARCHDATE,LANDINFO_LANDNO from t_leoa_landinfo; --定義遊標

   l_date t_leoa_landinfo.landinfo_researchdate%type; --宣告變數分別儲存t_leoa_landinfo的各列

   l_landNo t_leoa_landinfo.landinfo_landno%type;

begin

open cur_sel_all;

  loop                 --迴圈取數,並將遊標資料填充到返回紀錄集合中

   fetch cur_sel_all into l_date,l_landNo;

   exit when cur_sel_all%NOTFOUND; --迴圈退出條件

   if cur_sel_all%FOUND then --獲取資料

    update T_LEOA_BOOKFLOW t2 set FLOW_REASEARCHTIME = l_date where l_landNo = t2.landinfo_landno;

   end if;

  end loop;

close cur_sel_all;

end;

下面再分享一下另外一則遊標使用方法的程式碼,具體如下:

?

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

-- 宣告遊標;CURSOR cursor_name IS select_statement

--For 循環遊標

--(1)定義遊標

--(2)定義遊標變數

--(3)使用for迴圈來使用這個遊標

declare

    --型別定義

    cursor c_job

    is

    select empno,ename,job,sal

    from emp

    where job='MANAGER';

    --定義一個遊標變數v_cinfo c_emp%ROWTYPE ,該型別為遊標c_emp中的一行資料型別

    c_row c_job%rowtype;

begin

    for c_row in c_job loop

     dbms_output.put_line(c_row.empno||'-'||c_row.ename||'-'||c_row.job||'-'||c_row.sal);

    end loop;

end;

--Fetch遊標

--使用的時候必須要明確的開啟和關閉

declare

    --型別定義

    cursor c_job

    is

    select empno,ename,job,sal

    from emp

    where job='MANAGER';

    --定義一個遊標變數

    c_row c_job%rowtype;

begin

    open c_job;

     loop

      --提取一行資料到c_row

      fetch c_job into c_row;

      --判讀是否提取到值,沒取到值就退出

      --取到值c_job%notfound 是false

      --取不到值c_job%notfound 是true

      exit when c_job%notfound;

      dbms_output.put_line(c_row.empno||'-'||c_row.ename||'-'||c_row.job||'-'||c_row.sal);

     end loop;

    --關閉遊標

   close c_job;

end;

--1:任意執行一個update操作,用隱式遊標sql的屬性%found,%notfound,%rowcount,%isopen觀察update語句的執行情況。

    begin

     update emp set ENAME='ALEARK' WHERE EMPNO=7469;

     if sql%isopen then

      dbms_output.put_line('Openging');

      else

       dbms_output.put_line('closing');

       end if;

     if sql%found then

      dbms_output.put_line('遊標指向了有效行');--判斷遊標是否指向有效行

      else

       dbms_output.put_line('Sorry');

       end if;

       if sql%notfound then

        dbms_output.put_line('Also Sorry');

        else

         dbms_output.put_line('Haha');

         end if;

          dbms_output.put_line(sql%rowcount);

          exception

           when no_data_found then

            dbms_output.put_line('Sorry No data');

            when too_many_rows then

             dbms_output.put_line('Too Many rows');

             end;

declare

    empNumber emp.EMPNO%TYPE;

    empName emp.ENAME%TYPE;

    begin

     if sql%isopen then

      dbms_output.put_line('Cursor is opinging');

      else

       dbms_output.put_line('Cursor is Close');

       end if;

       if sql%notfound then

        dbms_output.put_line('No Value');

        else

         dbms_output.put_line(empNumber);

         end if;

         dbms_output.put_line(sql%rowcount);

         dbms_output.put_line('-------------');

         select EMPNO,ENAME into empNumber,empName from emp where EMPNO=7499;

         dbms_output.put_line(sql%rowcount);

        if sql%isopen then

        dbms_output.put_line('Cursor is opinging');

        else

        dbms_output.put_line('Cursor is Closing');

        end if;

         if sql%notfound then

         dbms_output.put_line('No Value');

         else

         dbms_output.put_line(empNumber);

         end if;

         exception

          when no_data_found then

           dbms_output.put_line('No Value');

           when too_many_rows then

            dbms_output.put_line('too many rows');

            end;

--2,使用遊標和loop迴圈來顯示所有部門的名稱

--遊標宣告

declare

    cursor csr_dept

    is

    --select語句

    select DNAME

    from Depth;

    --指定行指標,這句話應該是指定和csr_dept行型別相同的變數

    row_dept csr_dept%rowtype;

begin

    --for迴圈

    for row_dept in csr_dept loop

      dbms_output.put_line('部門名稱:'||row_dept.DNAME);

    end loop;

end;

--3,使用遊標和while迴圈來顯示所有部門的的地理位置(用%found屬性)

declare

    --遊標宣告

    cursor csr_TestWhile

    is

    --select語句

    select LOC

    from Depth;

    --指定行指標

    row_loc csr_TestWhile%rowtype;

begin

 --開啟遊標

    open csr_TestWhile;

    --給第一行喂資料

    fetch csr_TestWhile into row_loc;

    --測試是否有資料,並執行迴圈

     while csr_TestWhile%found loop

      dbms_output.put_line('部門地點:'||row_loc.LOC);

      --給下一行喂資料

      fetch csr_TestWhile into row_loc;

     end loop;

    close csr_TestWhile;

end;

select * from emp

     

--4,接收使用者輸入的部門編號,用for迴圈和遊標,打印出此部門的所有僱員的所有資訊(使用循環遊標)

--CURSOR cursor_name[(parameter[,parameter],...)] IS select_statement;

--定義引數的語法如下:Parameter_name [IN] data_type[{:=|DEFAULT} value]

declare

   CURSOR

   c_dept(p_deptNo number)

   is

   select * from emp where emp.depno=p_deptNo;

   r_emp emp%rowtype;

begin

    for r_emp in c_dept(20) loop

      dbms_output.put_line('員工號:'||r_emp.EMPNO||'員工名:'||r_emp.ENAME||'工資:'||r_emp.SAL);

    end loop;

end;

select * from emp 

--5:向遊標傳遞一個工種,顯示此工種的所有僱員的所有資訊(使用引數遊標)

declare

    cursor

    c_job(p_job nvarchar2)

    is

    select * from emp where JOB=p_job;

    r_job emp%rowtype;

begin

    for r_job in c_job('CLERK') loop

      dbms_output.put_line('員工號'||r_job.EMPNO||' '||'員工姓名'||r_job.ENAME);

    end loop;

end;

SELECT * FROM EMP

--6:用更新遊標來為僱員加佣金:(用if實現,建立一個與emp表一摸一樣的emp1表,對emp1表進行修改操作),並將更新前後的資料輸出出來

--http://zheng12tian.iteye.com/blog/815770

    create table emp1 as select * from emp;

declare

    cursor

    csr_Update

    is

    select * from emp1 for update OF SAL;

    empInfo csr_Update%rowtype;

    saleInfo emp1.SAL%TYPE;

begin

  FOR empInfo IN csr_Update LOOP

   IF empInfo.SAL<1500 THEN

    saleInfo:=empInfo.SAL*1.2;

    elsif empInfo.SAL<2000 THEN

    saleInfo:=empInfo.SAL*1.5;

    elsif empInfo.SAL<3000 THEN

    saleInfo:=empInfo.SAL*2;

   END IF;

   UPDATE emp1 SET SAL=saleInfo WHERE CURRENT OF csr_Update;

   END LOOP;

END;

--7:編寫一個PL/SQL程式塊,對名字以‘A'或‘S'開始的所有僱員按他們的基本薪水(sal)的10%給他們加薪(對emp1表進行修改操作)

declare

   cursor

   csr_AddSal

   is

   select * from emp1 where ENAME LIKE 'A%' OR ENAME LIKE 'S%' for update OF SAL;

   r_AddSal csr_AddSal%rowtype;

   saleInfo emp1.SAL%TYPE;

begin

   for r_AddSal in csr_AddSal loop

     dbms_output.put_line(r_AddSal.ENAME||'原來的工資:'||r_AddSal.SAL);

     saleInfo:=r_AddSal.SAL*1.1;

     UPDATE emp1 SET SAL=saleInfo WHERE CURRENT OF csr_AddSal;

   end loop;

end;

--8:編寫一個PL/SQL程式塊,對所有的salesman增加佣金(comm)500

declare

   cursor

     csr_AddComm(p_job nvarchar2)

   is

     select * from emp1 where  JOB=p_job FOR UPDATE OF COMM;

   r_AddComm emp1%rowtype;

   commInfo emp1.comm%type;

begin

  for r_AddComm in csr_AddComm('SALESMAN') LOOP

    commInfo:=r_AddComm.COMM+500;

     UPDATE EMP1 SET COMM=commInfo where CURRENT OF csr_AddComm;

  END LOOP;

END;

--9:編寫一個PL/SQL程式塊,以提升2個資格最老的職員為MANAGER(工作時間越長,資格越老)

--(提示:可以定義一個變數作為計數器控制遊標只提取兩條資料;也可以在宣告遊標的時候把僱員中資格最老的兩個人查出來放到遊標中。)

declare

  cursor crs_testComput

  is

  select * from emp1 order by HIREDATE asc;

  --計數器

  top_two number:=2;

  r_testComput crs_testComput%rowtype;

begin

  open crs_testComput;

    FETCH crs_testComput INTO r_testComput;

     while top_two>0 loop

       dbms_output.put_line('員工姓名:'||r_testComput.ENAME||' 工作時間:'||r_testComput.HIREDATE);

       --計速器減一

       top_two:=top_two-1;

       FETCH crs_testComput INTO r_testComput;

      end loop;

   close crs_testComput;

end;

--10:編寫一個PL/SQL程式塊,對所有僱員按他們的基本薪水(sal)的20%為他們加薪,

--如果增加的薪水大於300就取消加薪(對emp1表進行修改操作,並將更新前後的資料輸出出來)

declare

  cursor

    crs_UpadateSal

  is

    select * from emp1 for update of SAL;

    r_UpdateSal crs_UpadateSal%rowtype;

    salAdd emp1.sal%type;

    salInfo emp1.sal%type;

begin

    for r_UpdateSal in crs_UpadateSal loop

      salAdd:= r_UpdateSal.SAL*0.2;

      if salAdd>300 then

       salInfo:=r_UpdateSal.SAL;

       dbms_output.put_line(r_UpdateSal.ENAME||': 加薪失敗。'||'薪水維持在:'||r_UpdateSal.SAL);

       else

       salInfo:=r_UpdateSal.SAL+salAdd;

       dbms_output.put_line(r_UpdateSal.ENAME||': 加薪成功.'||'薪水變為:'||salInfo);

      end if;

      update emp1 set SAL=salInfo where current of crs_UpadateSal;

    end loop;

end;

--11:將每位員工工作了多少年零多少月零多少天輸出出來 

--近似

 --CEIL(n)函式:取大於等於數值n的最小整數

 --FLOOR(n)函式:取小於等於數值n的最大整數

 --truc的用法 http://publish.it168.com/2005/1028/20051028034101.shtml

declare

 cursor

  crs_WorkDay

  is

  select ENAME,HIREDATE, trunc(months_between(sysdate, hiredate) / 12) AS SPANDYEARS,

    trunc(mod(months_between(sysdate, hiredate), 12)) AS months,

    trunc(mod(mod(sysdate - hiredate, 365), 12)) as days

  from emp1;

 r_WorkDay crs_WorkDay%rowtype;

begin

  for  r_WorkDay in crs_WorkDay loop

  dbms_output.put_line(r_WorkDay.ENAME||'已經工作了'||r_WorkDay.SPANDYEARS||'年,零'||r_WorkDay.months||'月,零'||r_WorkDay.days||'天');

  end loop;

end;

--12:輸入部門編號,按照下列加薪比例執行(用CASE實現,建立一個emp1表,修改emp1表的資料),並將更新前後的資料輸出出來

-- deptno raise(%)

-- 10   5%

-- 20   10%

-- 30   15%

-- 40   20%

-- 加薪比例以現有的sal為標準

--CASE expr WHEN comparison_expr THEN return_expr

--[, WHEN comparison_expr THEN return_expr]... [ELSE else_expr] END

declare

   cursor

     crs_caseTest

     is

     select * from emp1 for update of SAL;

     r_caseTest crs_caseTest%rowtype;

     salInfo emp1.sal%type;

   begin

     for r_caseTest in crs_caseTest loop

     case

      when r_caseTest.DEPNO=10

      THEN salInfo:=r_caseTest.SAL*1.05;

      when r_caseTest.DEPNO=20

      THEN salInfo:=r_caseTest.SAL*1.1;

      when r_caseTest.DEPNO=30

      THEN salInfo:=r_caseTest.SAL*1.15;

      when r_caseTest.DEPNO=40

      THEN salInfo:=r_caseTest.SAL*1.2;

     end case;

     update emp1 set SAL=salInfo where current of crs_caseTest;

    end loop;

end;

--13:對每位員工的薪水進行判斷,如果該員工薪水高於其所在部門的平均薪水,則將其薪水減50元,輸出更新前後的薪水,員工姓名,所在部門編號。

--AVG([distinct|all] expr) over (analytic_clause)

---作用:

--按照analytic_clause中的規則求分組平均值。

 --分析函式語法:

 --FUNCTION_NAME(<argument>,<argument>...)

 --OVER

 --(<Partition-Clause><Order-by-Clause><Windowing Clause>)

   --PARTITION子句

   --按照表達式分割槽(就是分組),如果省略了分割槽子句,則全部的結果集被看作是一個單一的組

   select * from emp1

DECLARE

   CURSOR

   crs_testAvg

   IS

   select EMPNO,ENAME,JOB,SAL,DEPNO,AVG(SAL) OVER (PARTITION BY DEPNO ) AS DEP_AVG

   FROM EMP1 for update of SAL;

   r_testAvg crs_testAvg%rowtype;

   salInfo emp1.sal%type;

   begin

   for r_testAvg in crs_testAvg loop

   if r_testAvg.SAL>r_testAvg.DEP_AVG then

   salInfo:=r_testAvg.SAL-50;

   end if;

   update emp1 set SAL=salInfo where current of crs_testAvg;

   end loop;

end;

總結

以上就是本文關於Oracle中游標Cursor基本用法詳解的全部內容,希望對大家有所幫助,歡迎參閱:oracle資料庫匯入匯出命令解析ORACLE SQL語句優化技術要點解析淺談oracle中單引號轉義等,有什麼問題可以隨時留言,感謝大家!

您可能感興趣的文章:

原文連結:http://www.cnblogs.com/lcword/p/5857189.html

https://www.jb51.net/article/127299.htm