Parsing data rows in plsql - plsql

This is quite clumsy.
Initial info: There's a clumsy select query eventually returning 0 or 1 depending on several conditions. Usually it get's to select only one row (other data is cut of by where, etc.). The problem occurs when there's more rows to parse. So the data actually looks like follows:
Status
0
1
instead of
Status
1
Problem: Only one rowed data is needed in return, i.e. if there's a 1 present in any row, it should 1 returned, otherwise 0.
Condition: It should be done only in a query (no variables, ifs etc.).
Thanks in advance.

If you are sure that 1 and 0 are the only valuesbeing returned, Can't you use a max over this query to see any 1s..?
select max(id) result
from (
select 1 id from dual
union all
select 0 id from dual
)
RESULT
----------
1
1 select max(id)
2 from (
3 select 0 id from dual
4 union all
5 select 0 id from dual
6 union all
7 select 0 id from dual
8* )
SQL> /
MAX(ID)
----------
0

Related

SQLITE3 - Deleting rows that have multiple columns of the same value

I have an SQLite3 database from which I want to remove rows that have two fields of the same value.
It seems that I am able to select such values with this query:
SELECT * FROM mydb GROUP BY user_id, num HAVING COUNT(*) > 1
However I am not able to delete them.
DELETE FROM mydb WHERE user_id IN (SELECT * FROM mydb GROUP BY user_id, num HAVING COUNT(*) > 1)
returns a syntax error.
This is what I expect:
Example:
id user_id num
1 1 1
2 1 1
3 2 1
4 1 2
5 2 2
In this example id 1 and 2 have both columns (user_id and num) of the same value so they should be removed. Preferably, but not necessarily I would like to have a solution that would leave only one such row (doesn't matter which one).
Result:
id user_id num
2 1 1
3 2 1
4 1 2
5 2 2
Note: id is a primary key. user_id is a foreign key. num is an INTEGER.
You were having a syntax error because your IN operator has a single value on the left (user_id) but a table of non-single-value rows in the right side (SELECT *). Compare like with like; WHERE user_id IN (SELECT user_id ...) to avoid it.
Anyway, here's a query to delete all-but-newest:
DELETE FROM mydb
WHERE id NOT IN (
SELECT MAX(id) FROM mydb
GROUP BY user_id, num
);
The subquery will return the highest id for every unique (user_id, num) combination. Then we just delete all the other rows. I.e. in your example, the subquery would return 2, 3, 4, 5 as "correct", which would result in deletion of row 1.

over - partition by in SQLite

I have a table TABLE in SQLite database with columns DATE, GROUP. I want to select the first 10 entries in each group. After researching similar topics here on stackoverflow, I came up with the following query, but it runs very slowly. Any ideas how to make it faster?
select * from TABLE as A
where (select count(*) from TABLE as B
where B.DATE < A.DATE and A.GROUP == B.GROUP) < 10
This is the result of EXPLAIN QUERY PLAN (TABLE = clients_bets):
Here are a few suggestions :
Use a covering index (an index containing all the data needed in the subquery, in this case the group and date)
create index some_index on some_table(some_group, some_date)
Additionally, rewrite the subquery to make is less dependent on outer query :
select * from some_table as A
where rowid in (
select B.rowid
from some_table as B
where A.some_group == B.some_group
order by B.some_date limit 10 )
The query plan change from :
0 0 0 SCAN TABLE some_table AS A
0 0 0 EXECUTE CORRELATED LIST SUBQUERY 1
1 0 0 SEARCH TABLE some_table AS B USING COVERING INDEX idx_1 (some_group=?)
to
0 0 0 SCAN TABLE some_table AS A
0 0 0 EXECUTE CORRELATED SCALAR SUBQUERY 1
1 0 0 SEARCH TABLE some_table AS B USING COVERING INDEX idx_1 (some_group=? AND some_date<?)
While it is very similar, the query seems quite faster. I'm not sure why.

Trouble with Sqlite subquery

My CustomTags table may have a series of "temporary" records where Tag_ID is 0, and Tag_Number will have some five digit value.
Periodically, I want to clean up my Sqlite table to remove these temporary values.
For example, I might have:
Tag_ID Tag_Number
0 12345
0 67890
0 45678
1 12345
2 67890
In this case, I want to remove the first two records because they are duplicated with actual Tag_ID 1 and 2. But I don't want to remove the third record yet because it hasn't been duplicated yet.
I have tried a number of different types of subqueries, but I just can't get it working. This is the last thing I tried, but my database client complains of an unknown syntax error. (I have tried with and without AS as an alias)
DELETE FROM CustomTags t1
WHERE t1.Tag_ID = 0
AND (SELECT COUNT(*) FROM CustomTags t2 WHERE t1.Tag_Number = t2.Tag_Number) > 1
Can anyone offer some insight? Thank you
There are many options, but the simplest are probably to use EXISTS;
DELETE FROM CustomTags
WHERE Tag_ID = 0
AND EXISTS(
SELECT 1 FROM CustomTags c
WHERE c.Tag_ID <> 0 AND c.Tag_Number = CustomTags.Tag_Number
)
An SQLfiddle to test with.
...or NOT IN...
DELETE FROM CustomTags
WHERE Tag_ID = 0
AND Tag_Number IN (
SELECT Tag_Number FROM CustomTags WHERE Tag_ID <> 0
)
Another SQLfiddle.
With your dataset like so:
sqlite> select * from test;
tag_id tag_number
---------- ----------
1 12345
1 67890
0 12345
0 67890
0 45678
You can run:
delete from test
where rowid not in (
select a.rowid
from test a
inner join (select tag_number, max(tag_id) as mt from test group by tag_number) b
on a.tag_number = b.tag_number
and a.tag_id = b.mt
);
Result:
sqlite> select * from test;
tag_id tag_number
---------- ----------
1 12345
1 67890
Please do test this out with a few more test cases than you have to be entirely sure that's what you want. I'd recommend creating a copy of your database before you run this on a large dataset.

Increase row number every time a flag changes

I have gone through a similar post in Stack overflow...
but my query is :
If my table generates a flag in run time execution,then how can I increase Grp_number(generate run time) every time my flag changes.
my Oracle query:
Select emp_id,
Case when MOD(rownum/3)=1 and rownum>1 then 'Y' else 'N' as flag
from Transaction_table
Desired o/p Data format:
emp_id Flag GRP_number
1 N 1
2 N 1
3 N 1
4 Y 2
5 N 2
6 N 2
7 Y 3
You cannot reference a column in another column in the same select list. You need to use sub query to avoid INVALID IDENTIFIER error.
Do it like -
WITH DATA AS(
SELECT emp_id,
CASE
WHEN MOD(rownum/3)=1
AND rownum >1
THEN 'Y'
ELSE 'N' AS flag
FROM Transaction_table
)
SELECT emp_id, flag, SUM(gap) over (PARTITION BY person
ORDER BY DAY) grp
FROM(
SELECT emp_id, flag,
CASE WHEN flag = lag(flag) over (PARTITION BY person
ORDER BY DAY)
THEN 0
ELSE 1
END gap
FROM DATA)

generate 6 numbers between 1 and 2 in a 2:1 ratio in SQL or PL/SQL

how can i generate 6 numbers between 1 and 2 where 4 of the numbers will be 1 and the other 2 will be 2 in a random order i.e.
results
1
2
1
1
1
2
and also in a different ratio i.e. 3:2:1 for numbers between 1 and 3 for 12 numbers
i.e.
results
1
1
2
3
1
2
1
3
1
1
3
3
results don't have to be in this order but in the ratios as above in oracle SQL or PL/SQL
To get the ratios perfect you could do something like this - generate all the numbers, then sort in random order:
SELECT r
FROM (SELECT CASE
WHEN ROWNUM <=4 THEN 1
ELSE 2
END AS r
FROM DUAL
CONNECT BY LEVEL <= 6)
ORDER BY DBMS_RANDOM.value;
R
----------------------
2
1
1
2
1
1
I think this will work in straight SQL; it's horrifically inefficient, and a PL/SQL one might be less so. It's also completely static; differing ratios call for a different number of values selected.
select value
from (
select mod(value, 2) + 1 as value,
row_number() over (partition by
case mod(value, 2) = 1
then 1
else 0
end) as twos_row,
row_number() over (partition by
case mod(value, 2) = 0
then 1
else 0
end) as ones_row
from (select dbms_crypto.randominteger as value
from dba_objects
order by object_id
)
)
where twos_rows <= 2
or ones_rows <= 4
The inner-most select grabs a big stack of random numbers. The next query out determines whether that random value would be a 2 or a 1 by mod'ing the earlier random value. The last level of nesting just filters out all the rows after the correct number of that type of row has been returned.
This is untested and fragile. If you need a solution that's reliable and performance, I'd recommend PL/SQL, where you
loop
pick off random numbers
determine what partition in your set of values they'd fit into
keep them if that partition hasn't been satisfied
exit when all partitions have been satisfied.

Resources