Skip to content

Instantly share code, notes, and snippets.

@forstie
Last active April 12, 2023 21:16
Show Gist options
  • Save forstie/c43480aee468b2a0d8ff486d3018a948 to your computer and use it in GitHub Desktop.
Save forstie/c43480aee468b2a0d8ff486d3018a948 to your computer and use it in GitHub Desktop.
The request... change Db2 for i to allow ORDER BY on CREATE VIEW. Well, we aren't going to do that because its non-standard. This gist shows a path forward using the existing support.
--
-- Subject: Bringing ORDER to a VIEW
-- Author: Scott Forstie
-- Date : February, 2023
-- Features Used : This Gist uses UDTFs, SQL DDL, PIPE, SQL PL, SQL global variables
--
-- Notes:
-- ===============================================
-- The SQL Standard dictates that the ORDER BY clause cannot be
-- includes in the CREATE VIEW defintion.
--
-- Please keep in mind that by the letter of the query engine law, if you do NOT
-- include an ORDER BY, you are NOT guaranteed that rows will be returned in a
-- specific order. Period. That said, I have found this technique to be reliable-ish.
--
-- This gist shows how to circumnavigate!
stop;
--
-- Review the largest tables in System Limits
--
WITH X AS (SELECT ROW_NUMBER()
OVER(PARTITION BY SYSTEM_SCHEMA_NAME,
SYSTEM_OBJECT_NAME, SYSTEM_TABLE_MEMBER ORDER BY
CURRENT_VALUE DESC NULLS LAST) AS R, U.*
FROM QSYS2.SYSLIMITS U
WHERE LIMIT_ID = 15000)
SELECT LAST_CHANGE_TIMESTAMP, SYSTEM_SCHEMA_NAME,
SYSTEM_OBJECT_NAME, SYSTEM_TABLE_MEMBER,
CURRENT_VALUE
FROM X WHERE R = 1
ORDER BY CURRENT_VALUE DESC;
stop;
--
-- Boy, I love that query, but it seems like a better fit for a view!
--
create or replace view coolstuff.thebiggies as
WITH X AS (SELECT ROW_NUMBER()
OVER(PARTITION BY SYSTEM_SCHEMA_NAME,
SYSTEM_OBJECT_NAME, SYSTEM_TABLE_MEMBER ORDER BY
CURRENT_VALUE DESC NULLS LAST) AS R, U.*
FROM QSYS2.SYSLIMITS U
WHERE LIMIT_ID = 15000)
SELECT LAST_CHANGE_TIMESTAMP, SYSTEM_SCHEMA_NAME,
SYSTEM_OBJECT_NAME, SYSTEM_TABLE_MEMBER,
CURRENT_VALUE
FROM X WHERE R = 1;
-- ORDER BY CURRENT_VALUE DESC; <<=== the order by is not permitted!
stop;
--
-- Ew, I lost my ordering!
--
select *
from coolstuff.thebiggies;
stop;
--
-- Consolidate the query logic into a UDTF
--
create or replace function coolstuff.thebiggies ()
returns table (
last_chg timestamp, lib_name varchar(10) for sbcs data, file_name varchar(10) for sbcs data,
mbr_name varchar(10) for sbcs data, file_size bigint
)
not deterministic
external action
modifies sql data
not fenced
set option COMMIT = *NONE
begin
declare local_sqlcode integer;
declare local_sqlstate char(5);
declare not_found condition for '02000';
declare at_end integer default 0;
declare rows_returned integer default 0;
declare v_error integer default 0;
declare v_message_text varchar(70);
declare v_last_chg timestamp;
declare v_lib_name varchar(10) for sbcs data;
declare v_pgm_name varchar(10) for sbcs data;
declare v_mbr_name varchar(10) for sbcs data;
declare v_file_size bigint;
declare find_biggies_text varchar(1000) for sbcs data default ' WITH X AS (SELECT ROW_NUMBER()
OVER(PARTITION BY SYSTEM_SCHEMA_NAME,
SYSTEM_OBJECT_NAME, SYSTEM_TABLE_MEMBER ORDER BY
CURRENT_VALUE DESC NULLS LAST) AS R, U.*
FROM QSYS2.SYSLIMITS U
WHERE LIMIT_ID = 15000)
SELECT LAST_CHANGE_TIMESTAMP, SYSTEM_SCHEMA_NAME,
SYSTEM_OBJECT_NAME, SYSTEM_TABLE_MEMBER,
CURRENT_VALUE
FROM X WHERE R = 1
ORDER BY CURRENT_VALUE DESC';
declare c_find_biggies cursor for find_biggies_stmt;
declare continue handler for not_found set at_end = 1;
declare continue handler for SQLEXCEPTION
begin
get diagnostics condition 1
local_sqlcode = db2_returned_sqlcode, local_sqlstate = returned_sqlstate;
set v_message_text = 'coolstuff.thebiggies() failed with: ' concat local_sqlcode concat
' AND ' concat local_sqlstate;
signal sqlstate 'QPC01'
set message_text = v_message_text;
end;
prepare find_biggies_stmt from find_biggies_text;
open c_find_biggies;
set at_end = 0;
fetch from c_find_biggies
into v_last_chg, v_lib_name, v_pgm_name, v_mbr_name, v_file_size;
while (at_end = 0) do
pipe (v_last_chg, v_lib_name, v_pgm_name, v_mbr_name, v_file_size);
set rows_returned = rows_returned + 1;
set at_end = 0;
fetch from c_find_biggies
into v_last_chg, v_lib_name, v_pgm_name, v_mbr_name, v_file_size;
end while;
close c_find_biggies;
return;
end;
stop;
--
-- Boy, I love that query, but it seems like a better fit for a view!
--
create or replace view coolstuff.thebiggies as
select *
from table (
coolstuff.thebiggies()
);
stop;
-- Test the View
--
SELECT *
FROM coolstuff.thebiggies ;
stop;
--
-- Bonus topic
--
create or replace variable coolstuff.limit integer default 10;
stop;
--
-- Do it again
--
create or replace function coolstuff.thebiggies ()
returns table (
last_chg timestamp, lib_name varchar(10) for sbcs data, file_name varchar(10) for sbcs data,
mbr_name varchar(10) for sbcs data, file_size bigint
)
not deterministic
external action
modifies sql data
not fenced
set option COMMIT = *NONE
begin
declare local_sqlcode integer;
declare local_sqlstate char(5);
declare not_found condition for '02000';
declare at_end integer default 0;
declare rows_returned integer default 0;
declare v_error integer default 0;
declare v_message_text varchar(70);
declare v_last_chg timestamp;
declare v_lib_name varchar(10) for sbcs data;
declare v_pgm_name varchar(10) for sbcs data;
declare v_mbr_name varchar(10) for sbcs data;
declare v_file_size bigint;
declare find_biggies_text varchar(1000) for sbcs data default ' WITH X AS (SELECT ROW_NUMBER()
OVER(PARTITION BY SYSTEM_SCHEMA_NAME,
SYSTEM_OBJECT_NAME, SYSTEM_TABLE_MEMBER ORDER BY
CURRENT_VALUE DESC NULLS LAST) AS R, U.*
FROM QSYS2.SYSLIMITS U
WHERE LIMIT_ID = 15000)
SELECT LAST_CHANGE_TIMESTAMP, SYSTEM_SCHEMA_NAME,
SYSTEM_OBJECT_NAME, SYSTEM_TABLE_MEMBER,
CURRENT_VALUE
FROM X WHERE R = 1
ORDER BY CURRENT_VALUE DESC';
declare c_find_biggies cursor for find_biggies_stmt;
declare continue handler for not_found set at_end = 1;
declare continue handler for SQLEXCEPTION
begin
get diagnostics condition 1
local_sqlcode = db2_returned_sqlcode, local_sqlstate = returned_sqlstate;
set v_message_text = 'coolstuff.thebiggies() failed with: ' concat local_sqlcode concat
' AND ' concat local_sqlstate;
signal sqlstate 'QPC01'
set message_text = v_message_text;
end;
prepare find_biggies_stmt from find_biggies_text;
open c_find_biggies;
set at_end = 0;
fetch from c_find_biggies
into v_last_chg, v_lib_name, v_pgm_name, v_mbr_name, v_file_size;
while (at_end = 0 and rows_returned < coolstuff.limit) do
pipe (v_last_chg, v_lib_name, v_pgm_name, v_mbr_name, v_file_size);
set rows_returned = rows_returned + 1;
set at_end = 0;
fetch from c_find_biggies
into v_last_chg, v_lib_name, v_pgm_name, v_mbr_name, v_file_size;
end while;
close c_find_biggies;
return;
end;
stop;
--
-- Test the View, with a built-in LIMIT control
-- (returns top 10 by default)
--
SELECT *
FROM coolstuff.thebiggies ;
stop;
--
-- Test the View, with a built-in LIMIT control
-- (return top 20)
--
set coolstuff.limit = 20;
SELECT *
FROM coolstuff.thebiggies ;
stop;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment