Oracle Grand Total - oracle

I am having a query where I am selecting 4 columns. And, I want to put a grand total at the bottom of one of the columns, and not do any grouping:
SELECT customer_id, email, total_amount, order_date
FROM...................
I want to do a grand total of TOTAL_AMOUNT at the bottom, but not worry about any grouping. I'm not seeing how to do this using GROUPING or ROLLUP. I'm hoping not to have this as any running total in another oolumn, but as a grand total at the bottom.
Many thanks.

You can add a grand total row with a UNION ALL and a column to track if the row is for the grand total.
select customer_id, email, total_amount, order_date, 0 is_grand_total
from orders
union all
select null, null, sum(total_amount), null, 1 is_grand_total
from orders
order by is_grand_total, customer_id;
SQL Fiddle example.
(In my opinion, this is often a good way to add summary logic to queries. I'd rather have a slightly more complicated solution with one language (SQL), than a solution that involves two or more languages or applications.)

A simple SQL*Plus option:
SQL> break on report
SQL> compute sum of sal on report
SQL>
SQL> select deptno, ename, sal
2 from emp
3 where deptno = 10;
DEPTNO ENAME SAL
---------- ---------- ----------
10 CLARK 2450
10 KING 5001
10 MILLER 1300
----------
sum 8751
SQL>

Related

How to use a rowcount in select statement to modify the query to fetch data for 10 days , if rowcount is 0 for 5 days?

I need to modify my script using rowcount to check if the data in table or not?. Here, i write the query to select a data for last 5 days from current system date. But sometimes there is no data in table for 5 days. So i need to fetch for 10 day or more.
Query:
Select ep.ENTERPRISE_NAME||'|'||s.id||'|'||s.SUBMISSION_DATE||'|'||E.VALUE
from JOB_SUMMARY_EXT e, ob_summary s, enterprise ep
where e.id = s.id and e.name_res_key = 'Model'
and s.job_id in (select id from job_summary where
trunc(start_date) > trunc(sysdate) -10 and service_name ='Model2' )
I don't know how to modify my Query using rowcount. If rowcount is 0 then i want select data for 10 days.Otherwise it should to fetch for 5 days automatically. I want this to be done as single query.
It looks that you want to select the last 5 "days" from that table. So, why would you anchor to SYSDATE if there aren't rows for each of those days? I'd suggest another approach: literally, select last 5 days. Here's how.
As I don't have your tables, I'm using Scott's EMP table which contains information about employees. It is an ancient one so HIREDATE column is set to 1980s, but never mind that. Sorting employees by HIREDATE in descending order shows:
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> select ename, hiredate from emp order by hiredate desc;
ENAME HIREDATE
---------- ----------
ADAMS 12.01.1983 1.
SCOTT 09.12.1982 2.
MILLER 23.01.1982 3.
FORD 03.12.1981 4.
JAMES 03.12.1981 4.
KING 17.11.1981 5. --> I want to fetch rows up to KING
MARTIN 28.09.1981
TURNER 08.09.1981
CLARK 09.06.1981
BLAKE 01.05.1981
JONES 02.04.1981
WARD 22.02.1981
ALLEN 20.02.1981
SMITH 17.12.1980
14 rows selected.
SQL>
As you can see, the 4th date is shared by two employees so I want to include them both. DENSE_RANK analytic function helps:
SQL> with last5 as
2 (select ename,
3 job,
4 sal,
5 hiredate,
6 dense_rank() over (order by hiredate desc) rnk
7 from emp
8 )
9 select ename, job, sal, hiredate
10 from last5
11 where rnk <= 5;
ENAME JOB SAL HIREDATE
---------- --------- ---------- ----------
ADAMS CLERK 1100 12.01.1983
SCOTT ANALYST 3000 09.12.1982
MILLER CLERK 1300 23.01.1982
JAMES CLERK 950 03.12.1981
FORD ANALYST 3000 03.12.1981
KING PRESIDENT 5000 17.11.1981
6 rows selected.
SQL>
What does it do? The LAST5 CTE sorts employees (as above), DENSE_RANK ranks them; finally, the last SELECT (which begins at line #9) fetches desired rows.
In your case, that might look like this:
with last5 as
(select id,
dense_rank() over (order by start_date desc) rnk
from job_summary
where service_name = 'Model2'
)
select ep.enterprise_name,
s.id,
s.submission_date,
e.value
from job_summary_ext e
join ob_summary s on e.id = s.id
join last5 t on t.id = s.id
join enterprise ep on <you're missing join condition for this table>
where e.name_res_key = 'Model';
Note that you're missing join condition for the ENTERPRISE table; if that's really so, no problem - you'd use cross join for that table, but I somehow doubt that you want that.
Finally, as you use SQL*Plus, perhaps you don't need to concatenate all columns and separate them by the pipe | sign - set it as a column separator, e.g.
SQL> set colsep '|'
SQL>
SQL> select deptno, dname, loc from dept;
DEPTNO|DNAME |LOC
----------|--------------|-------------
10|ACCOUNTING |NEW YORK
20|RESEARCH |DALLAS
30|SALES |CHICAGO
40|OPERATIONS |BOSTON
SQL>
If you want to
return 10 last days if select count(*) returns 0, or
return 5 last days if select count(*) returns a positive number
then something like this might help (again based on Scott's EMP table):
with
tcnt as
-- count number of rows; use your own requirement, I'm checking
-- whether someone got hired today. In Scott's EMP table, nobody was
-- so CNT = 0
(select count(*) cnt
from emp
where hiredate >= trunc(sysdate)
)
select e.ename, e.job, e.sal, e.hiredate
from emp e cross join tcnt c
where e.hiredate >= case when c.cnt = 0 then trunc(sysdate) - 10
else trunc(sysdate) - 5
end;
Apply it to your tables; I don't know which of those 3 tables' count you want to check.
Tried to add in comments but it was too long for comments and Not clear on count based on but here is case in where clause substitute your count statement with nvl function
SELECT ep.ENTERPRISE_NAME||'|'||s.id||'|'||s.SUBMISSION_DATE||'|'||E.VALUE
FROM JOB_SUMMARY_EXT e,
ob_summary s,
enterprise ep
WHERE e.id = s.id
AND e.name_res_key = 'Model'
AND s.job_id IN
(SELECT id
FROM job_summary
WHERE service='Model'
AND trunc(start_date) >
CASE WHEN
(WRITE your SELECT COUNT criteria WITH NVL FUNCTION)<=0 THEN
trunc(sysdate) -10
ELSE trunc(sysdate)-5
END )

SELECT DISTINCT on one column and return multiple other columns in Oracle/TOAD

My goal is to get a distinct for Clm_Pd_Amt column only and return all other columns:
SELECT CLM_AMT, PAID_DATE, MBR, DISTINCT CLM_PD_AMT
FROM MY_CLAIMS
WHERE DATE >= '20200101
AND STATUS = 'CURRENT'
A GROUP BY is equivalent to a distinct, for example, the distinct list of departments in the EMP table can be found with (say)
SQL> select deptno, min(sal)
2 from emp
3 group by deptno
4 /
DEPTNO MIN(SAL)
---------- ----------
30 951
10 1300
20 800
But what if I want to know which employee had that minimum salary. Then you can use the KEEP clause to gather that as well, eg
SQL> select deptno, min(sal), min(empno) KEEP ( dense_rank FIRST order by sal) empno
2 from emp
3 group by deptno
4 /
DEPTNO MIN(SAL) EMPNO
---------- ---------- ----------
10 1300 7934
20 800 7369
30 951 7900
So using that approach, you should be able to adapt your query to get the distinct CLM_PD_AMT and then pick up the other columns with KEEP. This only works if you have a definition for which distinct CLM_PD_AMT means, ie, the smallest? the largest? etc

How to compare two tables in oracle when number of columns in tables differs

I have two tables in my database they are EXP1 and EXP2. I tried with the below query, this query is working when both the tables have same number of columns but my table EXP1 has 1000 columns n EXP2 has 1000+4.
select *
from
(
(select * from exp1
minus
select * from exp2)
union all
(select * from exp2
minus
select * from exp1)
);
INTRO: Below I show how one can do "by hand" what the tools (SQL Developer for example) can do much faster and much better. My interest in this (and yours!) is two-fold: learn and use some ideas that can help in many other problems; and understand what those tools do under the hood in the first place.
OK. Suppose you have two tables, and they have many columns in common (possibly not in the same order) and a few columns may be different - there may be a handful of columns in one table but not in the other. First you want to be able to look just at the common columns.
Then, suppose that's done. Now what's left of the two tables has many rows in common, but there are a few that are different. A row may exist in one table but not in the other, or two rows, one from each table, may be very similar but they may differ in just one or a small number of column values. Logically these are still one row in the first table but not the second, and the other row only in the second table but not in the first. However, let's say both tables have the same PK column - then you may have the same PK value in both tables, but at least one of the OTHER columns has different values for that PK value in the two tables. And, you want to find these differences between the two tables.
In what follows I will assume that if two columns, in the two tables, have the same name, they will also have the same data type. If that is not guaranteed in your case, it can be fixed with a little more work in the part where I identify the "common columns" - instead of matching them just by name, from the catalog views, they would have to be matched also by data type.
When you get to comparing rows in the two tables in the final step, (A minus B) union all (B minus A) works, but is not very efficient. Each table is read twice, and minus is an expensive operator. The more efficient solution, which I illustrate below, was discussed in a long thread on AskTom several years ago. Namely: collect all the rows from both tables (with union all), group by all the columns, and disregard the groups that have a count of 2. This means rows that were found in both tables, so they are duplicates in the union all! Actually, you will see a small additional trick to identify from which table the "non-duplicated" rows come. Add a column for "table_name" and in the final select, after grouping and keeping the groups with count(*) = 1, select max(table_name). You need an aggregate function (like max()) because you are grouping, but for these rows each group only has one row, so the max() is really just the table name.
The beauty of this approach is that it can be used to identify the common columns, too! In that case, we will compare rows from the USER_TAB_COLS view - we select column names that appear in either of the tables, and keep only the column names that are duplicates (so the column names appear in both tables). In that part of the solution, I also retrieve column_id, which is used to order the columns. Don't worry if you are not familiar with keep (dense_rank first...) - it's not really that complicated, but it's not that important either.
First let's set up a test case. I copy the EMP table from the SCOTT schema to my own schema, I replicate it (so now I have two copies, named EMP1 and EMP2), and I modify them slightly. I delete a different column from each, I delete a few (different) rows from each, and I modify one salary in one table. I will not show the resulting (slightly different) tables, but if you are following along, just select * from both and compare them before you continue reading.
Create the tables:
create table EMP1 as select * from scott.emp;
Table EMP1 created.
select * from EMP1;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
----- ---------- --------- ---- ------------------- ----- ------ -------
7369 SMITH CLERK 7902 1980-12-17 00:00:00 800 20
7499 ALLEN SALESMAN 7698 1981-02-20 00:00:00 1600 300 30
7521 WARD SALESMAN 7698 1981-02-22 00:00:00 1250 500 30
7566 JONES MANAGER 7839 1981-04-02 00:00:00 2975 20
7654 MARTIN SALESMAN 7698 1981-09-28 00:00:00 1250 1400 30
7698 BLAKE MANAGER 7839 1981-05-01 00:00:00 2850 30
7782 CLARK MANAGER 7839 1981-06-09 00:00:00 2450 10
7788 SCOTT ANALYST 7566 1987-04-19 00:00:00 3000 20
7839 KING PRESIDENT 1981-11-17 00:00:00 5000 10
7844 TURNER SALESMAN 7698 1981-09-08 00:00:00 1500 0 30
7876 ADAMS CLERK 7788 1987-05-23 00:00:00 1100 20
7900 JAMES CLERK 7698 1981-12-03 00:00:00 950 30
7902 FORD ANALYST 7566 1981-12-03 00:00:00 3000 20
7934 MILLER CLERK 7782 1982-01-23 00:00:00 1300 10
Modify them slightly:
create table EMP2 as select * from EMP1;
Table EMP2 created.
alter table emp1 drop column hiredate;
Table EMP1 altered.
alter table emp2 drop column comm;
Table EMP2 altered.
delete from EMP1 where ename like 'A%';
2 rows deleted;
delete from EMP2 where sal >= 3000;
3 rows deleted
update EMP2 set sal = 2950 where empno = 7698;
1 row updated
commit;
At this point you would do well to select * from EMP1; and select * from EMP2; and compare.
Now let's find out what columns the two tables have left in common.
select column_name,
min(column_id) keep(dense_rank first order by table_name) as col_id
from user_tab_cols
where table_name in ('EMP1', 'EMP2')
group by column_name
having count(*) = 2
order by col_id;
COLUMN_NAME COL_ID
----------- ------
EMPNO 1
ENAME 2
JOB 3
MGR 4
SAL 5
DEPTNO 7
6 rows selected
Perfect, so now we can compare the two tables, but only after we "project" them along the common columns only.
select max(table_name) as table_name, EMPNO, ENAME, JOB, MGR, SAL, DEPTNO
from (
select 'EMP1' as table_name, EMPNO, ENAME, JOB, MGR, SAL, DEPTNO from EMP1
union all
select 'EMP2' as table_name, EMPNO, ENAME, JOB, MGR, SAL, DEPTNO from EMP2
)
group by EMPNO, ENAME, JOB, MGR, SAL, DEPTNO
having count(*) = 1
order by EMPNO, ENAME, JOB, MGR, SAL, DEPTNO, table_name;
TABLE_NAME EMPNO ENAME JOB MGR SAL DEPTNO
---------- ----- ---------- --------- ------ ------ --------
EMP2 7499 ALLEN SALESMAN 7698 1600 30
EMP1 7698 BLAKE MANAGER 7839 2850 30
EMP2 7698 BLAKE MANAGER 7839 2950 30
EMP1 7788 SCOTT ANALYST 7566 3000 20
EMP1 7839 KING PRESIDENT 5000 10
EMP2 7876 ADAMS CLERK 7788 1100 20
EMP1 7902 FORD ANALYST 7566 3000 20
7 rows selected
The output is pretty much what we needed. Notice the first column, which tells us where the "unpaired" row comes from; and note BLAKE, who has different salary in the two tables (and the first column helps us to see what salary he has in which table).
This looks perfect so far, but what to do when you have 1000 columns? You could put it together in C or Java etc., using the result from the "common columns" query above - or you could do it all in Oracle, with dynamic SQL.
As far as I know, there is no set limit on the length of the text of an SQL statement in Oracle; the documentation says "The limit on how long a SQL statement can be depends on many factors, including database configuration, disk space, and memory" (and probably on your Oracle version, which they didn't mention). In any case, it will be more than 4000 characters, so we need to work with CLOB. In particular, we can't use listagg() - we need a workaround. I use xmlagg() below. Then, the documentation says if you concatenate text and at least one operand is CLOB the result will be CLOB; if that doesn't work for you, you may have to wrap the smaller text fragments within to_clob(). The "dynamic SQL" query below will produce the full text of the query I used above; you will simply copy it and paste it back into your front-end and execute it. You may have to delete wrapping double-quotes or such, depending on your front-end and settings.
First here is how we can create a (potentially very long) string, the list of common column names, which is repeated five times in the final query - just look again at the "final query" we used to compare the two tables above.
with
common_cols ( column_name, col_id ) as (
select column_name,
min(column_id) keep(dense_rank first order by table_name) as col_id
from user_tab_cols
where table_name in ('EMP1', 'EMP2')
group by column_name
having count(*) = 2
),
col_string ( str ) as (
select rtrim(xmlcast(xmlagg(xmlelement(e, column_name, ', ') order by col_id)
as clob), ', ') from common_cols
)
select * from col_string;
STR
-----------------------------------
EMPNO, ENAME, JOB, MGR, SAL, DEPTNO
And finally the full dynamic SQL query (the result is exactly the query I used to compare EMP1 and EMP2 on their common columns earlier):
with
common_cols ( column_name, col_id ) as (
select column_name,
min(column_id) keep(dense_rank first order by table_name) as col_id
from user_tab_cols
where table_name in ('EMP1', 'EMP2')
group by column_name
having count(*) = 2
),
col_string ( str ) as (
select rtrim(xmlcast(xmlagg(xmlelement(e, column_name, ', ') order by col_id)
as clob), ', ') from common_cols
)
select 'select max(table_name) as table_name, ' || str || chr(10) ||
'from (' || chr(10) ||
' select ''EMP1'' as table_name, ' || str || ' from EMP1' || chr(10) ||
' union all' || chr(10) ||
' select ''EMP2'' as table_name, ' || str || ' from EMP2' || chr(10) ||
' )' || chr(10) ||
'group by ' || str || chr(10) ||
'having count(*) = 1' || chr(10) ||
'order by ' || str || ', table_name;' as comp_sql_str
from col_string;

How can i select just one row in Oracle by row id?

I have used mysql database in my application, but I want to migrate to Oracle.
The problem is in that query:
select * from users limit ?,1;"
That query returns every row one by one depending on ?.
How can i do that in oracle?
On Oracle 12c, you could use the row limiting feature using FETCH FIRST clause.
SQL> SELECT empno, sal, deptno FROM emp ORDER BY empno DESC
2 FETCH FIRST 1 ROWS ONLY;
EMPNO SAL DEPTNO
---------- ---------- ----------
7934 1300 10
SQL>
Prior 12c solution is ROWNUM, however, if you want the row to be first sorted, then you need to do it in a sub-query -
SQL> SELECT empno, sal, deptno FROM
2 ( SELECT * FROM emp ORDER BY empno DESC
3 ) WHERE ROWNUM = 1;
EMPNO SAL DEPTNO
---------- ---------- ----------
7934 1300 10
SQL>
If the order doesn't matter to you, if you just want any random row, simply use ROWNUM.
Depending on your requirement, you could also use ANALYTIC functions such as ROW_NUMBER, RANK, DENSE_RANK.
select * from (select rownum r, u.* from users u ) where r=1;
or if you want it sorted (replace x by columnnumber or columnname):
select * from (select rownum r, u.* from users u order by x) where r=1;

the number of same record until this row

Is it possible to retrieve current number of same record for each row by using oracle pl/sql?
For example,
I have class table which consists of id, name, age columns
I want to have the sequence of student with the same name and age entering the class, assuming that id is countering up without altering data structure.
Thanks.
Regards,
Jim
Not sure I entirely get what you're asking for; you have an odd turn of phrase. An example of input data and expected result is always useful.
Perhaps something like this:
select id, name, age
from your_table
where (name, age) in
( select name. age
from your_table
group by name, age
having count(id) > 1 )
order by name, age, id
/
You could solve this with analytics. However, you still need an outer query to filter out the records which aren't duplicated, so I'm not sure what you'd gain:
select * from (
select id, name, age
, count(id) over (partition by name, age) as dup_count
from your_table )
where dup_count > 1
order by name, age, id
/
I'm not sure either, but it sounds to me something related to analytic functions. Take this as an example, look at srlno column, calculated using analytic functions:
SELECT empno, deptno, hiredate,
ROW_NUMBER( ) OVER (PARTITION BY
deptno ORDER BY hiredate
NULLS LAST) SRLNO
FROM emp
WHERE deptno IN (10, 20)
ORDER BY deptno, SRLNO;
EMPNO DEPTNO HIREDATE SRLNO
------ ------- --------- ----------
7782 10 09-JUN-81 1
7839 10 17-NOV-81 2
7934 10 23-JAN-82 3
7369 20 17-DEC-80 1
7566 20 02-APR-81 2
7902 20 03-DEC-81 3
7788 20 09-DEC-82 4
7876 20 12-JAN-83 5
More on analyltic functions:
http://www.orafaq.com/node/55
Remember, is a better approach if you can achieve your goal with a SQL instead of PL/SQL.

Resources