Selecting tables with one record in Oracle - sql

select table_name,
to_number(extractvalue(xmltype(dbms_xmlgen.getxml('select
count(*) c from '||owner||'.'||table_name)),'/ROWSET/ROW/C')) as count
from all_tables
I would like to get those with one record in the table.

The query crashes only when the filter predicate is applied. It's probably a bug in some query rewrite optimization. If you wrap the query in a block with the materialize hint, it seems to bypass this behavior.
with workaround as(
select /*+ materialize */
owner
,table_name
,to_number(extractvalue(xmltype(dbms_xmlgen.getxml('select count(*) c from ' || owner || '.' || table_name || ' where rownum <= 2')),'/ROWSET/ROW/C')) as row_count
from all_tables
where owner = '<your-schema>'
)
select owner, table_name, row_count
from workaround
where row_count = 1;
I also found some potential to improve the performance of this query. If you only want tables with exactly one record, there is really no need to count every single record in the table. If you add the predicate rownum <= 2 Oracle will stop scanning as soon as it has found two records. So the count will be either:
0, meaning empty table
1, meaning exactly one record
2, meaning more than 1 record
Edit to show how the optimization work:
-- Creating tables
create table t0(c number);
create table t1(c number);
create table t2(c number);
create table t3(c number);
insert into t1 values(1);
insert into t2 values(1);
insert into t2 values(2);
insert into t3 values(1);
insert into t3 values(2);
insert into t3 values(3);
commit;
SQL:
/*
|| Without rownum you can filter on any rowcount you want
*/
select *
from (select 'T0' as t, count(*) as row_count from t0 union all
select 'T1' as t, count(*) as row_count from t1 union all
select 'T2' as t, count(*) as row_count from t2 union all
select 'T3' as t, count(*) as row_count from t3
)
where row_count = 1 -- Return tables having exactly 1 record.
;
/*
|| With rownum <= 1 Oracle will stop counting after it found one row.
|| So the rowcount will be either 0 or 1.
|| row_count = 0 means that the table is empty
|| row_count = 1 means that the table is NOT empty.
||
|| The Rownum predicate prevents us from knowing if there are 2,3,4 or 5 million records.
*/
select *
from (select 'T0' as t, count(*) as row_count from t0 where rownum <= 1 union all
select 'T1' as t, count(*) as row_count from t1 where rownum <= 1 union all
select 'T2' as t, count(*) as row_count from t2 where rownum <= 1 union all
select 'T3' as t, count(*) as row_count from t3 where rownum <= 1
)
where row_count = 1 -- Return tables having at least one record
;
/*
|| With rownum <= 2 Oracle will stop counting after it found two rows.
|| So the rowcount will be either 0, 1 or 2.
|| row_count = 0 means that the table is empty
|| row_count = 1 means that the table has exactly 1 record
|| row_count = 2 means that the table has more than 1 record
||
|| The Rownum predicate prevents us from knowing if there are exactly two records, or 3,4,5 etcetera
*/
select *
from (select 'T0' as t, count(*) as row_count from t0 where rownum <= 2 union all
select 'T1' as t, count(*) as row_count from t1 where rownum <= 2 union all
select 'T2' as t, count(*) as row_count from t2 where rownum <= 2 union all
select 'T3' as t, count(*) as row_count from t3 where rownum <= 2
)
where row_count = 1 -- Return tables having exactly one record
;

If your query is giving the data you want, but you just want to see the results with count equal to 1, you can wrap the query like this:
select * from (
select table_name,
to_number(extractvalue(xmltype(dbms_xmlgen.getxml('select
count(*) c from '||owner||'.'||table_name)),'/ROWSET/ROW/C')) as count
from all_tables
) where count = 1

SELECT * FROM
(
SELECT table_name,
TO_NUMBER(EXTRACTVALUE(XMLTYPE(dbms_xmlgen.getxml('select count(*) c from '||OWNER||'.'||table_name)),'/ROWSET/ROW/C')) AS cnt
FROM all_tables
)
WHERE cnt = 1;
or
SELECT * FROM
(
SELECT table_name,
TO_NUMBER(EXTRACTVALUE(XMLTYPE(dbms_xmlgen.getxml('select count(*) c from '||OWNER||'.'||table_name)),'/ROWSET/ROW/C')) AS cnt
FROM all_tables
) slct
GROUP BY slct.table_name
HAVING slct.cnt = 1;

Related

select 2 recors in case 2 exists

I have 2 rows
code name
1 cake
2 chocolate
This query give me both results
select * from table a where a.code=2 or a.code =1
If one of these records doesn't show, I don't want to retrieve anything.
select * from table a where a.code=2 or a.code =1
and exists ( select 1 from table b where a.code=b.code )
You can use an analytic function:
SELECT code,
name
FROM (
SELECT a.*,
COUNT(DISTINCT code) OVER () AS num_codes
FROM table_name a
WHERE a.code IN (1,2)
)
WHERE num_codes = 2;
Which, for the sample data:
CREATE TABLE table_name (code, name) AS
SELECT 1, 'cake' FROM DUAL UNION ALL
SELECT 2, 'chocolate' FROM DUAL;
Outputs:
CODE
NAME
1
cake
2
chocolate
If you:
DELETE FROM table_name WHERE code = 1;
And run the query again, it outputs:
CODE
NAME
db<>fiddle here
Simple check the count distict
select * from tab1
where code in (1,2)
and (select count(distinct code) from tab1 where code in (1,2)) = 2;
If you want to discard the case where the table has duplicated rows, e.g. 1,1,2
add an other predicate filter
and (select count(*) from tab1 where code in (1,2)) = 2

Merge three tables in Select query by rule 3, 2, 1 records from each table

Merge three tables in a Select query by rule 3, 2, 1 records from each table as follows:
TableA: ID, FieldA, FieldB, FieldC,....
TableB: ID, FieldA, FieldB, FieldC,....
TableC: ID, FieldA, FieldB, FieldC,....
ID : auto number in each table
FieldA will be unique in all three tables.
I am looking for a Select query to merge three tables as follows:
TOP three records from TableA sorted by ID
TOP two records from TableB sorted by ID
TOP 1 record from TableC sorted by ID
Repeat this until select all records from all three tables.
If some table has fewer records or does not meet the criteria, ignore that and continue with others.
My attempt:
I did it totally through programming way, like cursors and If conditions inside a SQL Server stored procedure.
It makes delay.
This requires a formula that takes row numbers from each table and transforms it into a series of integers that skips the desired values.
In the query below, I am adding some CTE for the sake of shortening the formula. The real magic is in the UNION. Also, I am adding an additional field for your control. Feel free to get rid of it.
WITH A_Aux as (
SELECT 'A' As FromTable, ROW_NUMBER() OVER (ORDER BY ID) AS RowNum, TableA.*
FROM TableA
), B_Aux AS (
SELECT 'B' As FromTable, ROW_NUMBER() OVER (ORDER BY ID) AS RowNum, TableB.*
FROM TableB
), C_Aux AS (
SELECT 'C' As FromTable, ROW_NUMBER() OVER (Order BY ID) AS RowNum, TableC.*
FROM TableC
)
SELECT *
FROM (
SELECT RowNum+3*FLOOR((RowNum-1)/3) As ColumnForOrder, A_Aux.* FROM A_Aux
UNION ALL
SELECT 3+RowNum+4*FLOOR((RowNum-1)/2), B_Aux.* FROM B_Aux
UNION ALL
SELECT 6*RowNum, C_Aux.* FROM C_Aux
) T
ORDER BY ColumnForOrder
PS: note the pattern Offset + RowNum + (6-N) * Floor((RowNum-1)/N) to group N records together (it of course simplifies a lot for TableC).
PPS: I don't have a SQL server at hand to test it. Let me know if there is a syntax error.
You may try this..
GO
select * into #temp1 from (select * from table1) as t1
select * into #temp2 from (select * from table2) as t2
select * into #temp3 from (select * from table3) as t3
select * into #final from (select col1, col2, col3 from #temp1 where 1=0) as tb
declare #i int
set #i=1
while( (select COUNT(*) from #temp1)>#i)
Begin
;with ct1 as (
select ROW_NUMBER() over (order by id) as Slno, * from #temp1
),ct2 as (
select ROW_NUMBER() over (order by id) as Slno, * from #temp2
),ct3 as (
select ROW_NUMBER() over (order by id) as Slno, * from #temp3
),cfinal as (
select top 3 * from #temp1
union all
select top 2 * from #temp2
union all
select top 1 * from #temp3
)
insert into #final ( col1 , col2, col3 )
select col1, col2, col3 from cfinal
delete from #temp1 where id in (select top 3 ID from #temp1)
delete from #temp2 where id in (select top 2 ID from #temp2)
delete from #temp3 where id in (select top 1 ID from #temp3)
set #i = #i+1
End
Select * from #final
Drop table #temp1
Drop table #temp2
Drop table #temp3
GO
First create temp table for all 3 tables with each insert delete the inserted record and this will result you the desired result, if nothing is missing from my side.
Please see to this if this works.
There is not a lot of information to go with here, but I assume you can use UNION to combine multiple statements.
SELECT * TableA ORDER BY ID DESC OFFSET 3 ROWS
UNION
SELECT * TableB ORDER BY ID DESC OFFSET 2 ROWS
UNION
SELECT * TableC ORDER BY ID DESC OFFSET 1 ROWS
Execute and see if this works.
/AF
From my understanding, I create three temp tables as ta, tb, tc.
select * into #ta from (
select 'A' a
union all
select 'A' a
union all
select 'A' a
union all
select 'A' a
union all
select 'A' a
union all
select 'A' a
union all
select 'A' a
) a
select * into #tb from (
select 'B' b
union all
select 'B'
union all
select 'B'
union all
select 'B'
union all
select 'B'
) b
select * into #tc from (
select 'C' c
union all
select 'C'
union all
select 'C'
union all
select 'C'
union all
select 'C'
) c
If tables match you tables, then the output looks like A,A,A,B,B,C,A,A,A,B,B,C,A,B,C,C,C
T-SQL
declare #TAC int = (select count (*) from #ta) -- Table A Count = 7
declare #TBC int = (select count (*) from #tb) -- Table B Count = 5
declare #TAR int = #TAC % 3 -- Table A Reminder = 1
declare #TBR int = #TBC % 2 -- Table B Reminder = 1
declare #TAQ int = (#TAC - #TAR) / 3 -- Table A Quotient = (7 - 1) / 3 = 2, is will passed on NTILE
-- So we gonna split as two group (111), (222)
declare #TBQ int = (#TBC - #TBR) / 2 -- Table B Quotient = (5 - 1) / 2 = 2, is will passed on NTILE
-- So we gonna split as two group (11), (22)
select * from (
select *, NTILE (#TAQ) over ( order by a) FirstOrder, 1 SecondOrder from (
select top (#TAC - #TAR) * from #ta order by a
) ta -- 6 rows are obtained out of 7.
union all
select *, #TAQ + 1, 1 from (
select top (#TAR) * from #ta order by a desc
) ta -- Remaining one row is obtained. Order by desc is must
-- Here FirstOrder is next value of previous value.
union all
select *, NTILE (#TBQ) over ( order by b), 2 from (
select top (#TBC - #TBR) * from #tb order by b
) tb
union all
select *, #TBQ + 1, 2 from (
select top (#TBR) * from #tb order by b desc
) tb
union all
select *, ROW_NUMBER () over (order by c), 3 from #tc
) abc order by FirstOrder, SecondOrder
Let me explain the T-SQL:
Before that, FYR: NTILE and Row Number
Get the count.
Find the Quotient which will pass to NTILE function.
Order by the NTILE value and static.
Note:
I am using SQL Server 2017.
If T-SQL works fine, then you need to change the column in order by <yourcolumn>.

Need help identifying dups in the table

What I have:
data_source_1 table
data_source_2 table
data_sources_view view
About tables:
data_source_1:
has no dups:
db=# select count(*) from (select distinct * from data_source_1);
count
--------
543243
(1 row)
db=# select count(*) from (select * from data_source_1);
count
--------
543243
(1 row)
data_source_2:
has no dups:
db=# select count(*) from (select * from data_source_2);
count
-------
5304
(1 row)
db=# select count(*) from (select distinct * from data_source_2);
count
-------
5304
(1 row)
data_sources_view:
has dups:
db=# select count(*) from (select distinct * from data_sources_vie);
count
--------
538714
(1 row)
db=# select count(*) from (select * from data_sources_view);
count
--------
548547
(1 row)
The view is simple as:
CREATE VIEW data_sources_view
AS SELECT *
FROM (
(
SELECT a, b, 'data_source_1' as source
FROM data_source_1
)
UNION ALL
(
SELECT a, b, 'data_source_2' as source
FROM data_source_2
)
);
What I want to know:
How is that possible to have dups in a view where source tables doesn't have dups + 'data_source_x' as source eliminates the possibility of overlapping data.
How to identify dups?
What I've tried:
db# create table t1 as select * from data_sources_view;
SELECT
db=#
db=# create table t2 as select distinct * from data_sources_view;
SELECT
db=# create table t3 as select * from t1 minus select * from t2;
SELECT
db=# select 't1' as table_name, count(*) from t1 UNION ALL
db-# select 't2' as table_name, count(*) from t2 UNION ALL
db-# select 't3' as table_name, count(*) from t3;
table_name | count
------------+--------
t1 | 548547
t3 | 0
t2 | 538714
(3 rows)
Database:
Redshift (PostgreSQL)
The reason is because your data sources have more than two columns. If you do these counts:
select count(*) from (select distinct a, b from data_source_1);
and
select count(*) from (select distinct a, b from data_source_2);
You should find that they are different from the count(*) you get on the same table.
UNION vs UNION ALL
UNION - If the data exist in the TOP Query it's suppressed in the bottom query.
OUTPUT
FOO
UNION ALL - The data repeats as the data exist in both tables (shows both records)
OUTPUT
FOO
FOO

How can I collectively select 100 rows from 3 different tables?

I have 3 tables containing similar rows of data.
I need to select 100 rows from all of the three tables with the following conditions:
No more than 25 rows can be selected from Table A --> (name it count_a)
No more than 40 rows can be selected from Table B --> (count_b)
Any number of rows can be selected from Table C (count_c) but the number should be
count_c = 100 - (count_a + count_b)
Here is what I tried:
SELECT * FROM
(
SELECT * FROM TABLE_A WHERE ROWNUM <= 25
UNION ALL
SELECT * FROM TABLE_B WHERE ROWNUM <= 40
UNION ALL
SELECT * FROM TABLE_C
) WHERE ROWNUM <=100
But the query is too slow and does not always give me 100 rows.
Try to add WHERE ROWNUM <= 100 to the last select:
SELECT * FROM
(
SELECT TABLE_A.*, 1 as OrdRow FROM TABLE_A WHERE ROWNUM <= 25
UNION ALL
SELECT TABLE_B.*, 2 as OrdRow FROM TABLE_B WHERE ROWNUM <= 40
UNION ALL
SELECT TABLE_C.*, 3 as OrdRow FROM TABLE_C WHERE ROWNUM <= 100
) WHERE ROWNUM <=100
ORDER BY OrdRow;
Also you can try:
SELECT * FROM TABLE_A WHERE ROWNUM <= 25
UNION ALL
SELECT * FROM TABLE_B WHERE ROWNUM <= 40
UNION ALL
SELECT * FROM TABLE_C WHERE ROWNUM <=
100
-
(select count(*) TABLE_A WHERE ROWNUM <= 25)
-
(select count(*) TABLE_B WHERE ROWNUM <= 40)
Try like this,
SELECT * FROM
(
SELECT * FROM table_a where rownum <=25
UNION ALL
SELECT * FROM table_b WHERE ROWNUM <= 40
UNION ALL
SELECT * FROM table_c WHERE ROWNUM <= 100 - ((SELECT count(*) FROM table_a WHERE ROWNUM <= 25) + (SELECT count(*) FROM table_b WHERE ROWNUM <= 40))
);
Technically, you'd have to do something like this in order to guarantee that you'll always get rows from TABLE_A and TABLE_B if they exist:
SELECT * FROM (
SELECT * FROM (
SELECT 'A' t, TABLE_A.* FROM TABLE_A WHERE ROWNUM <= 25
UNION ALL
SELECT 'B' t, TABLE_B.* FROM TABLE_B WHERE ROWNUM <= 40
UNION ALL
SELECT 'C' t, TABLE_C.* FROM TABLE_C
) ORDER BY t
) WHERE ROWNUM <= 100;
This is because the optimizer is allowed to run the subqueries in any order it likes - e.g. in parallel.
With regard to performance, I suspect that the sort op will not add too much time to the execution time because it's only sorting a maximum of 100 rows anyway.
Yes ... Execution speed is a nightmare to every developer or programmer ...
In that case you can try like ... i think it will speed up your query more
DECLARE #Table_A_Row_Count INT, #Table_B_Row_Count INT,#RemainCount INT=0
--Getting count of primary tables
SELECT #Table_A_Row_Count= COUNT(1) FROM TABLE_A
SELECT #Table_B_Row_Count= COUNT(1) FROM TABLE_B
--Calculating remaining colections
IF #Table_A_Row_Count+#Table_B_Row_Count<100
BEGIN
SET #RemainCount=100-(#Table_A_Row_Count+#Table_B_Row_Count)
END
ELSE
BEGIN
--You might do somthing like this if First
--and second table covering 100 rows
SET #Table_B_Row_Count=100-#Table_A_Row_Count
END
--Finaly getting 100 rows
SELECT top #Table_A_Row_Count * FROM TABLE_A
UNION ALL
SELECT top #Table_B_Row_Count * FROM TABLE_B
UNION ALL
SELECT Top #RemainCount * FROM TABLE_C

How to select a record if the query returns one row, or select no record if the query returns more rows?

I require to select a row if there is only one row exists, if there are more rows, it should select 0 rows.
If you're using PL/SQL, then selecting the column using select-into will throw a too_many_rows exception if there's more than one row returned:
declare
var table.column%type;
begin
select column
into var
from table
where ...;
end;
If you want to do this just using SQL, then you can do something like:
select *
from
(select s.*, count(*) over () c
from
(select *
from table
where ...
and rownum <= 2
) s
)
where c = 1
UPDATE
As DazzaL says in the comments, the reason for the rownum <= 2 restriction is to short-circuit the query if there's more than 2 rows in the result set. This can give significant performance benefits if the dataset is large.
I came up with this, just for the heck of it, using a CTE
With counter as
( select count(any_field) as cnt from your_query
)
SELECT
your_query
WHERE exists (SELECT cnt from Counter WHERE cnt=1)
1 row when there's 1 record - http://sqlfiddle.com/#!4/84c7b/2
0 rows when more than 1 rec - http://sqlfiddle.com/#!4/95c4a/1
EDIT
or if you want to avoid repeating the whole query... an example :
(using the schema from sqlfiddle http://sqlfiddle.com/#!4/6a2d8/117 )
With results as
( select * from montly_sales_totals
),
counter as
( SELECT count(name) as cnt FROM results
)
SELECT *
FROM results
WHERE exists (SELECT cnt from Counter WHERE cnt=5)
SELECT fld1, fld2
FROM (SELECT COUNT(*) over() cnt ,fld1, fld2 FROM tbl WHERE fld1 = 'key')
WHERE cnt = 1
I require to select a row if there is only one row exists, if there
are more rows, it should select 0 rows.
I assume the table contains only the row(s) you are interested to see (or not to see), in that case I would write something like
select *
from table1
where 1 = (select count(1)
from table1
)
In case you want to see only one row from a subset of results from your table, I would go for something like:
with t as ( select *
from table1
where [put here your condition]
)
select *
from t
where 1 = (select count(1)
from t
)
Try this:
SELECT f1,f2
FROM Table
WHERE (f1 = #f1) AND (f2=#f2) AND (f3=#f3)
GROUP BY f1,f2
HAVING (COUNT(*) = 1)
DECLARE COL_COUNT NUMBER;
BEGIN
COL_COUNT: = 0 ;
SELECT COUNT (1) INTO COL_COUNT FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '(ur table name)';
IF COL_COUNT = 0 THEN
EXECUTE IMMEDIATE ('select * from dual') ;
END IF;
END;
Try this:
SELECT col1, col2 FROM
(SELECT count(id) as 'cnt', col1, col2 FROM table_name WHERE col1='value')
WHERE cnt=1;