Fetch first 10 rows with IN Operation - sql

I want to fetch first 10 rows as per below condition and also where tabname = 'TABLE1' and 'TABLE2' (if these tables are not in first 10 rows even)
First 10 rows as per below condition is working fine:
db2 "select substr(a.tabname,1,30) as TABNAME,
> a.rows_read as RowsRead,
> (a.rows_read / (b.commit_sql_stmts + b.rollback_sql_stmts + 1)) as TBRRTX,
> (b.commit_sql_stmts + b.rollback_sql_stmts) as TXCNT
> from sysibmadm.snaptab a, sysibmadm.snapdb b
> where a.dbpartitionnum = b.dbpartitionnum
> and b.db_name = 'LIVE'
> order by a.rows_read desc fetch first 10 rows only"
TABNAME ROWSREAD TBRRTX TXCNT
------------------------------ -------------------- -------------------- --------------------
XOUTMSGLOG 43845129056 41 1049571334
SCHSTATUS 35336410261 33 1049571334
ADDRESS 26817245226 25 1049571334
CATGRPDESC 25628156703 24 1049571334
ORDERITEMS 23945555619 22 1049571334
ORDERS 10656700035 10 1049571334
XPAYINSTDATA 10555959906 10 1049571334
OFFER 10426958061 9 1049571334
SCHBRDCST 10286981444 9 1049571334
ATTRVALDESC 8327058697 7 1049571334
10 record(s) selected.
Now, requirement is to have 'TABLE1' and 'TABLE2' (if these tables are not in first 10 rows even) so how to add this condition in above statement?
So that it looks like below:
TABNAME ROWSREAD TBRRTX TXCNT
------------------------------ -------------------- -------------------- --------------------
XOUTMSGLOG 43845129056 41 1049571334
SCHSTATUS 35336410261 33 1049571334
ADDRESS 26817245226 25 1049571334
CATGRPDESC 25628156703 24 1049571334
ORDERITEMS 23945555619 22 1049571334
ORDERS 10656700035 10 1049571334
XPAYINSTDATA 10555959906 10 1049571334
OFFER 10426958061 9 1049571334
SCHBRDCST 10286981444 9 1049571334
ATTRVALDESC 8327058697 7 1049571334
TABLE1 81444 1 10495713341
TABLE2 97 1 1049571334
12 record(s) selected.

A generic solution for whatever SELECT statement.
SELECT *
FROM
(
-- Whatever SELECT statement with the desired expression
-- in the ORDER BY clause of the ROW_NUMBER function
SELECT
TABNAME, COLCOUNT
, ROW_NUMBER () OVER (ORDER BY COLCOUNT DESC) AS RN_
FROM SYSCAT.TABLES
WHERE TABSCHEMA = 'SYSIBM'
)
WHERE RN_ <= 5 OR TABNAME IN ('SQLSCHEMAS', 'SQLTABLES')
ORDER BY RN_
TABNAME
COLCOUNT
RN_
SYSROUTINES
84
1
SYSTABLES
83
2
SYSINDEXES
71
3
SYSPLAN
69
4
ROUTINES
56
5
SQLSCHEMAS
11
73
SQLTABLES
11
74
In your case:
SELECT *
FROM
(
SELECT substr(a.tabname,1,30) as TABNAME,
a.rows_read as RowsRead,
--(a.rows_read / (b.commit_sql_stmts + b.rollback_sql_stmts + 1)) as TBRRTX,
--(b.commit_sql_stmts + b.rollback_sql_stmts) as TXCNT
A.ROWS_READ / NULLIF (B.TOTAL_APP_COMMITS + B.TOTAL_APP_ROLLBACKS, 0) as TBRRTX,
B.TOTAL_APP_COMMITS + B.TOTAL_APP_ROLLBACKS as TXCNT,
ROW_NUMBER () OVER (order by a.rows_read desc) AS RN_
FROM
/*
sysibmadm.snaptab a, sysibmadm.snapdb b
where a.dbpartitionnum = b.dbpartitionnum
--and b.db_name = 'LIVE'
--order by a.rows_read desc fetch first 10 rows ONLY
*/
-- It's better to use new MON functions instead of old SNAP ones
TABLE (MON_GET_TABLE (NULL, NULL, -2)) a
JOIN TABLE (MON_GET_DATABASE (-2)) b ON b.MEMBER = a.MEMBER
)
WHERE RN_ <= 10 OR TABNAME IN ('TABLE1', 'TABLE2')
ORDER BY RN_
BTW:
sysibmadm.snapdb returns info for a currently connected database only, since it's based on SELECT ... FROM TABLE (SYSPROC.SNAP_GET_DB(' ')) (space as a parameter value means current database). So, the b.db_name = 'LIVE' predicate can only make your query return no rows, if you use wrong database name.

Related

Row wise comparisons between two Oracle table

I'm a beginner & trying to find a solution for the below issue
I have two table with same number of column, same names and same datatype.
I'm sorting both the table by time and trying to do row wise comparison (including data in each row) using not exist operation to identify the mismatch of records. If all the rows in both table are same then I need to return True else False.
Note: No relationship between the tables to do Join.
This is how I understood the question.
Sample tables: they have the same number of columns, with same names, same datatypes - but, you didn't say whether they contain the same number of rows. Mine don't:
SQL> select * from t1 order by id;
ID NAME COUNTRY
---------- ------ -------
1 Little Croatia
2 Foot Hungary
3 Scott UK
SQL> select * from t2 order by id;
ID NAME COUNTRY
---------- ------ -------
1 Little Croatia
2 Michel France
SQL>
The idea is to compare SHA1 hash values computed on concatenated values from both tables for comparative rows (which ones? Those that share the same row number).
SQL> set serveroutput on
SQL> declare
2 l_cnt_1 number;
3 l_cnt_2 number;
4 l_cnt number;
5 --
6 cursor c1 is
7 select id, name, country
8 from (select id, name, country,
9 row_number() over (order by id) rn
10 from t1)
11 order by rn;
12 cursor c2 is
13 select id, name, country
14 from (select id, name, country,
15 row_number() over (order by id) rn
16 from t2)
17 order by rn;
18 c1r c1%rowtype;
19 c2r c2%rowtype;
20 h1 raw(20);
21 h2 raw(20);
22 begin
23 -- which table is smaller (by number of rows)?
24 select count(*) into l_cnt_1 from t1;
25 select count(*) into l_cnt_2 from t2;
26 l_cnt := least(l_cnt_1, l_cnt_2);
27
28 open c1;
29 open c2;
30 -- loop as many times as there are rows in a smaller table; it doesn't make
31 -- sense comparing rows that don't have a "pair"
32 for i in 1 .. l_cnt loop
33 fetch c1 into c1r;
34 fetch c2 into c2r;
35
36 -- SHA1 hash
37 select sys.dbms_crypto.hash(utl_raw.cast_to_raw
38 (c1r.id || c1r.name || c1r.country), sys.dbms_crypto.hash_sh1),
39 sys.dbms_crypto.hash(utl_raw.cast_to_raw
40 (c2r.id || c2r.name || c2r.country), sys.dbms_crypto.hash_sh1)
41 into h1, h2
42 from dual;
43
44 dbms_output.put_line('Comparing');
45 dbms_output.put_line('- T1: ' || c1r.id ||', '|| c1r.name ||', '|| c1r.country);
46 dbms_output.put_line('- T2: ' || c2r.id ||', '|| c2r.name ||', '|| c2r.country);
47
48 if h1 = h2 then
49 dbms_output.put_line('- Result: match');
50 else
51 dbms_output.put_line('- Result: no match');
52 end if;
53 end loop;
54
55 close c1;
56 close c2;
57 end;
58 /
Result is:
Comparing
- T1: 1, Little, Croatia
- T2: 1, Little, Croatia
- Result: match
Comparing
- T1: 2, Foot, Hungary
- T2: 2, Michel, France
- Result: no match
PL/SQL procedure successfully completed.
SQL>

select query - eliminate rows with duplicate column value on condition

I have a select query that ends up with results like:
ID COMPLIANT
------------------
10 0
12 0
29 0
29 1
43 1
44 1
44 0
How can I get results without these duplicate ID rows, on the condition that if an ID has already been marked as COMPLIANT once (a 1 instead of a 0), the duplicate rows with COMPLIANT=0 do not appear? I'd want:
ID COMPLIANT
------------------
10 0
12 0
29 1
43 1
44 1
How about aggregation?
select id, max(complaint) as complaint
from t
group by id;
This returns one row per id. If you can have multiple complaints -- and you want all of those -- than an alternative is:
select id, complaint
from t
where complaint = 1
union all
select id, complaint
from t
where not exists (select 1 from t t2 where t2.id = t.id and t2.complaint = 1);
this will work:
select id, max(complaint)
from tablename
group by id;

Identify same amounts over different users

Consider the following table Orders:
OrderID Name Amount
-----------------------
1 A 100
2 A 5
3 B 32
4 C 4000
5 D 701
6 E 32
7 F 200
8 G 100
9 H 12
10 I 17
11 J 100
12 J 100
13 J 11
14 A 5
I need to identify, for each unique 'Amount', if there are 2 or more users that have ordered that exact amount, and then list the details of those orders. So the desired output would be:
OrderID Name Amount
---------------------
1 A 100
8 G 100
11 J 100
12 J 100
3 B 32
6 E 32
please note that user A has ordered 2 x an order of 5 (order 2 and 14) but this shouldn't be in the output as it is within the same user. Only if another user would have made a order of 5, it should be in the output.
Can anyone help me out?
I would just use exists:
select o.*
from orders o
where exists (select 1
from orders o2
where o2.amount = o.amount and o2.name <> o.name
);
You can do :
select t.*
from table t
where exists (select 1 from table t1 where t1.amount = t.amount and t1.name <> t.name);
If you want only selected field then
SELECT Amount,name,
count(*) AS c
FROM TABLE
GROUP BY Amount, name
HAVING c > 1
ORDER BY c DESC
if you want full row
select * from table where Amount in (
select Amount, name from table
group by Amount, name having count(*) > 1)

Highest per each group

It's hard to show my actual table and data here so I'll describe my problem with a sample table and data:
create table foo(id int,x_part int,y_part int,out_id int,out_idx text);
insert into foo values (1,2,3,55,'BAK'),(2,3,4,77,'ZAK'),(3,4,8,55,'RGT'),(9,10,15,77,'UIT'),
(3,4,8,11,'UTL'),(3,4,8,65,'MAQ'),(3,4,8,77,'YTU');
Following is the table foo:
id x_part y_part out_id out_idx
-- ------ ------ ------ -------
3 4 8 11 UTL
3 4 8 55 RGT
1 2 3 55 BAK
3 4 8 65 MAQ
9 10 15 77 UIT
2 3 4 77 ZAK
3 4 8 77 YTU
I need to select all fields by sorting the highest id of each out_id.
Expected output:
id x_part y_part out_id out_idx
-- ------ ------ ------ -------
3 4 8 11 UTL
3 4 8 55 RGT
3 4 8 65 MAQ
9 10 15 77 UIT
Using PostgreSQL.
Postgres specific (and fastest) solution:
select distinct on (out_id) *
from foo
order by out_id, id desc;
Standard SQL solution using a window function (second fastest)
select id, x_part, y_part, out_id, out_idx
from (
select id, x_part, y_part, out_id, out_idx,
row_number() over (partition by out_id order by id desc) as rn
from foo
) t
where rn = 1
order by id;
Note that both solutions will only return each id once, even if there are multiple out_id values that are the same. If you want them all returned, use dense_rank() instead of row_number()
select *
from foo
where (id,out_id) in (
select max(id),out_id from foo group by out_id
) order by out_id
Finding max(val) := finding the record for which no larger val exists:
SELECT *
FROM foo f
WHERE NOT EXISTS (
SELECT 317
FROM foo nx
WHERE nx.out_id = f.out_id
AND nx.id > f.id
);

Table transformation / field parsing in PL/SQL

I have de-normalized table, something like
CODES
ID | VALUE
10 | A,B,C
11 | A,B
12 | A,B,C,D,E,F
13 | R,T,D,W,W,W,W,W,S,S
The job is to convert is where each token from VALUE will generate new row. Example:
CODES_TRANS
ID | VALUE_TRANS
10 | A
10 | B
10 | C
11 | A
11 | B
What is the best way to do it in PL/SQL without usage of custom pl/sql packages, ideally with pure SQL?
Obvious solution is to implement it via cursors. Any ideas?
Another alternative is to use the model clause:
SQL> select id
2 , value
3 from codes
4 model
5 return updated rows
6 partition by (id)
7 dimension by (-1 i)
8 measures (value)
9 ( value[for i from 0 to length(value[-1])-length(replace(value[-1],',')) increment 1]
10 = regexp_substr(value[-1],'[^,]+',1,cv(i)+1)
11 )
12 order by id
13 , i
14 /
ID VALUE
---------- -------------------
10 A
10 B
10 C
11 A
11 B
12 A
12 B
12 C
12 D
12 E
12 F
13 R
13 T
13 D
13 W
13 W
13 W
13 W
13 W
13 S
13 S
21 rows selected.
I have written up to 6 alternatives for this type of query in this blogpost: http://rwijk.blogspot.com/2007/11/interval-based-row-generation.html
Regards,
Rob.
I have a pure SQL solution for you.
I adapted a trick I found on an old Ask Tom site, posted by Mihail Bratu. My adaptation uses regex to tokenise the VALUE column, so it requires 10g or higher.
The test data.
SQL> select * from t34
2 /
ID VALUE
---------- -------------------------
10 A,B,C
11 A,B
12 A,B,C,D,E,F
13 R,T,D,W1,W2,W3,W4,W5,S,S
SQL>
The query:
SQL> select t34.id
2 , t.column_value value
3 from t34
4 , table(cast(multiset(
5 select regexp_substr (t34.value, '[^(,)]+', 1, level)
6 from dual
7 connect by level <= length(value)
8 ) as sys.dbms_debug_vc2coll )) t
9 where t.column_value != ','
10 /
ID VALUE
---------- -------------------------
10 A
10 B
10 C
11 A
11 B
12 A
12 B
12 C
12 D
12 E
12 F
13 R
13 T
13 D
13 W1
13 W2
13 W3
13 W4
13 W5
13 S
13 S
21 rows selected.
SQL>
Based on Celko's book, here is what I found and it's working well!
SELECT
TABLE1.ID
, MAX(SEQ1.SEQ) AS START_POS
, SEQ2.SEQ AS END_POS
, COUNT(SEQ2.SEQ) AS PLACE
FROM
TABLE1, V_SEQ SEQ1, V_SEQ SEQ2
WHERE
SUBSTR(',' || TABLE1.VALUE || ',', SEQ1.SEQ, 1) = ','
AND SUBSTR(',' || TABLE1.VALUE || ',', SEQ2.SEQ, 1) = ','
AND SEQ1.SEQ < SEQ2.SEQ
AND SEQ2.SEQ <= LENGTH(TABLE1.VALUE)
GROUP BY TABLE1.ID, TABLE1.VALUE, SEQ2.SEQ
Where V_SEQ is a static table with one field:
SEQ, integer values 1 through N, where N >= MAX_LENGTH(VALUE).
This is based on the fact the the VALUE is wrapped by ',' on both ends, like this:
,A,B,C,D,
If your tokens are fixed length (like in my case) I simply used PLACE field to calculate the actual string. If variable length, use start_pos and end_pos
So, in my case, tokens are 2 char long, so the final SQL is:
SELECT
TABLE1.ID
, SUBSTR(TABLE1.VALUE, T_SUB.PLACE * 3 - 2 , 2 ) AS SINGLE_VAL
FROM
(
SELECT
TABLE1.ID
, MAX(SEQ1.SEQ) AS START_POS
, SEQ2.SEQ AS END_POS
, COUNT(SEQ2.SEQ) AS PLACE
FROM
TABLE1, V_SEQ SEQ1, V_SEQ SEQ2
WHERE
SUBSTR(',' || TABLE1.VALUE || ',', SEQ1.SEQ, 1) = ','
AND SUBSTR(',' || TABLE1.VALUE || ',', SEQ2.SEQ, 1) = ','
AND SEQ1.SEQ < SEQ2.SEQ
AND SEQ2.SEQ <= LENGTH(TABLE1.VALUE)
GROUP BY TABLE1.ID, TABLE1.VALUE, SEQ2.SEQ
) T_SUB
INNER JOIN
TABLE1 ON TABLE1.ID = T_SUB.ID
ORDER BY TABLE1.ID, T_SUB.PLACE
Original Answer
In SQL Server TSQL we parse strings and make a table object. Here is sample code - maybe you can translate it.
http://rbgupta.blogspot.com/2007/10/tsql-parsing-delimited-string-into.html
Second Option
Count the number of commas per row. Get the Max number of commas. Let's say that in the entire table you have a row with 5 commas max. Build a SELECT with 5 substrings. This will make it a set based operation and should be much faster than a rbar.