Can I limit results using IN and COUNT? - oracle

I need to limit the results from a query, but I can't implement any example I've seen using count and rownum. Given this table
rec error
___ _____
1 123
2 123
3 456
4 456
5 456
6 456
7 456
8 456
9 456
10 789
11 789
12 789
13 789
This table has many more rows with many different error codes. I'm using this to get the records i need:
select rec, error from table where error in (123,456,789)
But say I want to only return no more than 2 records per error. I'm not sure how to do this. If I was only looking for a single error, I could simply use count or rownum. Not sure how to do it when using the IN condition.

Do you care which two rows you get for any particular error code? Something like this will get you the two rows for each error code with the smallest rec value. If you change the ORDER BY in the ROW_NUMBER analytic function, you can change which two rows are returned.
SELECT rec,
error
FROM (SELECT rec,
error,
row_number() over (partition by error
order by rec asc) rnk
FROM your_table_name)
WHERE error in (123,456,789)
AND rnk <= 2

Related

Validating decimals in a column - oracle sqldeveloper

Im trying to come up with a test that validates decimals in a particular column (with 220000 records). For example for column A there shouldn't be any values with more decimals than 2, 1 is also ok.
for example :
Column A (datatype varchar)
48528.64
135082.54
5249.1
I tried with round function but than I get an error saying invalid number.
Also I would like to be able to change the number of decimals I put in the test to use with different columns
For example
Its 1 big table with all columns having datatype VARCHAR2(2000 char)
examples for columns:
total amount (value should have no more than 2 decimals)
48528.64
135082.54
349.1123 (not OK)
Balance (value should have no more than 2 decimals)
45428.64
1895082.11
5249.1483 (not OK)
Loan (value should have no more than 6 decimals)
100.64
88999.11654
1000.178875554 (not OK)
For each column I want to set up a seperate test that checks if the value is within the number of decimals allowed. So preferable a select statement with a where clause where I can adjust the numbers of decimals so I end up with all records having 1 or 2 decimals, or all the records that have more than 2 decimals
Invalid number error is due to the fact that you have something that isn't a number in that column, so when you apply numeric function to it, Oracle complains. That's what you get when you store numbers as strings. Don't do that.
Anyway, here's one option which shows what you might try to do: as these are strings, calculate number of digits right of the decimal point.
SQL> select * From test;
A
--------------------
48528.64 -- OK
135082.54 -- OK
5249.1 -- OK
1.2345 -- not OK
-25.553 -- not OK
SQL> select *
2 from test
3 where length(regexp_substr(a, '\d+$')) > 2;
A
--------------------
1.2345
-25.553
SQL>
If there are several columns and you'd like to check each of them using a separate table which holds allowed number of decimals, then you could do something like this:
SQL> with
2 big (total, balance, loan) as
3 (select 48528.64 , 45428.64 , 100.64 from dual union all
4 select 135082.54 , 1895082.11 , 88999.11654 from dual union all
5 select 349.1123 , 5249.1483, 1000.178875554 from dual
6 ),
7 septest (tdec, bdec, ldec) as
8 (select 2, 2, 6 from dual)
9 select
10 b.total,
11 case when length(regexp_substr(b.total,'\d+$')) > s.tdec then 'Not OK'
12 else 'OK'
13 end total_ok,
14 --
15 b.balance,
16 case when length(regexp_substr(b.balance,'\d+$')) > s.bdec then 'Not OK'
17 else 'OK'
18 end balance_ok,
19 --
20 b.loan,
21 case when length(regexp_substr(b.loan,'\d+$')) > s.ldec then 'Not OK'
22 else 'OK'
23 end loan_ok
24 from big b cross join septest s;
TOTAL TOTAL_OK BALANCE BALANCE_OK LOAN LOAN_OK
---------- ---------- ---------- ---------- ---------- ----------
48528,64 OK 45428,64 OK 100,64 OK
135082,54 OK 1895082,11 OK 88999,1165 OK
349,1123 Not OK 5249,1483 Not OK 1000,17888 Not OK
SQL>
Lines #1 - 8 represent sample data; you already have that. Query you actually need begins at line #9.

Consolidate rows

I'm trying to cut down on rows a report has. There are 2 assets that return on this query but I want them to show up on one row.
Basically if dc.name LIKE '%CT/PT%' then I want it to be same row as the asset. The SP.SVC_PT_ID is the common field to join them.
There will be times when there is no dc.name LIKE '%CT/PT%' however I still want the DV.MFG_SERIAL_NUM to populated just with a Null to the right.
Select SP.SVC_PT_ID, SP.DEVICE_ID, DV.MFG_SERIAL_NUM, dc.name,
substr(dc.name,26)
From EIP.SVC_PT_DEVICE_REL SP,
eip.device_class dc,
EIP.DEVICE DV
Where SP.EFF_START_TIME < To_date('20170930', 'YYYYMMDD') + 1
and SP.EFF_END_TIME is null
and dc.id = DV.device_class_id
and DV.ID = SP.device_id
ORDER BY SP.SVC_PT_ID, DV.MFG_SERIAL_NUM;
I'm not sure I understand what you are saying; test case would certainly help. You said that query you posted returns two rows (only if we saw which ones ...) but you want them to be displayed as the image you attached to the message.
Generally speaking, you can do that using an aggregate function (such as MAX) on certain column(s), along with the GROUP BY clause that contains the rest of them.
Just for example:
select svc_pt_id, max(ctpt_name) ctpt_name, sum(ctpt_multipler) ctpt_multipler
from ...
group by svc_pt_id
As I said: a test case would help people who'd want to answer the question. True - someone might have understood it far better than I did and will provide assistance nevertheless.
EDIT: after you posted sample data (which, by the way, don't match screenshot you posted previously), maybe something like this might do the job: use analytic function to check whether name contains CT/PT; if so, take its data. Otherwise, display both rows.
SQL> with test as (
2 select 14 svc_pt_id, 446733 device_id, 'Generic Electric' name from dual union
3 select 14, 456517, 'Generic CT/PT, Multiplier' from dual
4 ),
5 podaci as
6 (select svc_pt_id, device_id, name,
7 rank() over (partition by svc_pt_id
8 order by case when instr(name, 'CT/PT') > 1 then 1
9 else 2
10 end) rnk
11 from test
12 )
13 select svc_pt_id, device_id, name
14 from podaci
15 where rnk = 1;
SVC_PT_ID DEVICE_ID NAME
---------- ---------- -------------------------
14 456517 Generic CT/PT, Multiplier
SQL>
My TEST table (created by WITH factoring clause) would be the result of your current query.

convert string of a column to multiple rows

For data like below
Col1
----
1
23
34
124
Output should be like below
Out
1
2
3
4
I tried the below hierarchical query but its giving repeated data
select substr(col1, level, 1)
from table1
connect by level <= length(col1);
I can't use distinct as this is sample and main table where I have to use this query has quite large data.
Thanks

How to know if a record DOESN'T exists on a table in Oracle

I'm dealing whit this for a couple of hours and I can't find the way to get the answer.
I've a table with a maximun of 4 records for a product (let's call it that way) for a diferent period (column name with a number). I'm trying to return the ones that DO NOT has a particular type of CONSUMPTION_TYPE_ID. But it doesn't work.
I'll explain it simple. I've a table with these fields (there are more, but these one are just fine)
product_id - CONSUMPTION_TYPE_ID - consumption_period
123 103 1
123 104 1
123 107 1
123 108 1
I need to return the ones that don't has one particular type of consumption, let's say that the type 107 is missing (the row doesn't exists), the select query should show the other 3 or any present. I don't mind doing the same select 4 times, I could also try to do a cursor for it and use loop to check every one. The point is, that the type of query with "not in" or "not exists" doesn't work. It gives me a result like the one given below, but when I query the "consumption_period" it shows me the missing "CONSUMPTION_TYPE_ID" and that's because the "not in" clause it's only hidding the results.
this is what I need.
select * from t1 where CONSUMPTION_TYPE_ID != 108;
product_id - CONSUMPTION_TYPE_ID - consumption_period
123 103 1
123 104 1
123 107 1
I hope you can help me with this. I'm stucked, it maybe simple, but I'm having one of those stucked times. Thanks in advance for any help
You probably should've posted that NOT EXISTS query that doesn't work, because that is the right way to do this.
If I got your requirements right: all products that do not have a record for a specific consumption_type_id.
SELECT DISTINCT product_id
FROM t1 t
WHERE NOT EXISTS
(SELECT 1 FROM t1
WHERE t.product_id = product_id
AND Consumption_Type_ID = ?)
The obvious answer here is to search for CONSUMPTION_TYPE_ID = 108 and have the surrounding code check for a lack of rows, rather than the existence of rows.
If you really need a row return for each consumption_type_id that's not in this table, then you should probably be selecting from the lookup table for consumption_type_id:
select *
from consumption_type ct
where not exists (select *
from t1
where t1.consumption_type_id = ct.consumption_type_id)
and ct.consumption_type_id = 108

How to select two max value from different records that has same ID for every records in table

i have problem with this case, i have log table that has many same ID with diferent condition. i want to select two max condition from this. i've tried but it just show one record only, not every record in table.
Here's my records table:
order_id seq status____________________
1256 2 4
1256 1 2
1257 0 2
1257 3 1
Here my code:
WITH t AS(
SELECT x.order_id
,MAX(y.seq) AS seq2
,MAX(y.extern_order_status) AS status
FROM t_order_demand x
JOIN t_order_log y
ON x.order_id = y.order_id
where x.order_id like '%12%'
GROUP BY x.order_id)
SELECT *
FROM t
WHERE (t.seq2 || t.status) IN (SELECT MAX(tt.seq2 || tt.status) FROM t tt);
this query works, but sometime it gave wrong value or just show some records, not every records.
i want the result is like this:
order_id seq2 status____________________
1256 2 4
1257 3 2
I think you just want an aggregation:
select d.order_id, max(l.seq2) as seq2, max(l.status) as status
from t_order_demand d join
t_order_log l
on d.order_id = l.order_id
where d.order_id like '%12%'
group by d.order_id;
I'm not sure what your final where clause is supposed to do, but it appears to do unnecessary filtering, compared to what you want.

Resources