oracle sum by column without using union - sql

I have this table: (supply table: how many products in storages)
Storage_id product_id amount
1 1000 55
1 1005 1
...
29 1000 3
29 1421 21
29 1566 0
30 1259 921
I should write a query to have this result:
storage_id product_id amount
1 1000 55
2 1000 61
...
30 1000 10
total_except_storage_30 1000 1505
1 1001 1
2 1001 50
...
30 1001 56
total_except_storage_30 1001 1251
...
"Total_except_storage_30" has the total of every product in storages except storage number 30. For example first "total_except_storage_30" is for product_id 1000 in all storages except storage_id 30 and the second is for product_id 1001.
*** I am not allowed to use "Union".
I tried to use full outer join but this did not work and the result is without "total_except_storage_30":
Select t.Storage_id, t.product_id, t.amount
from myTable t full outer join
(
select 'total_except_storage_30' as storage_id, product_id, sum(amount)
from myTable
group by product_id
) total
on t.storage_id = total.storage_id

Something like this should do it
select
product,
storage_id,
sum(case when storage_id != 30 then sal end)
from scott.emp
group by grouping sets (
(storage_id,product),
(product)
)
order by product, storage_id;
Here's an example of that using the standard EMP, DEPT
SQL> select
2 deptno,
3 empno,
4 sum(sal)
5 from scott.emp
6 group by grouping sets (
7 (empno,deptno),
8 (deptno)
9 )
10 order by deptno, empno;
DEPTNO EMPNO SUM(SAL)
---------- ---------- ----------
10 7782 2450
10 7839 5000
10 7934 1300
10 8750
20 7369 800
20 7566 2975
20 7788 3000
20 7876 1100
20 7902 3000
20 10875
30 7499 1600
30 7521 1250
30 7654 1250
30 7698 2850
30 7844 1500
30 7900 950
30 9400
17 rows selected.
You can see you get subtotals throughout

Related

SQL statement that repeats itself until there are no more results

I have the below existing statement. It finds parts that exist within other builds. The problem is, my user wants to go further until there are no more results.
For example.
We look up where PartA is used.
We find it is used inside of PartB and PartC.
Then we want to run the query again to find where PartB is used and PartC.
If PartB is not used anywhere else (No results found), we want to return PartB as part of the results.
But if PartC is used somewhere else, we want to keep going until we get no result for each part.
I am not sure if this is even possible so I thought I would ask here.
select ms.contract site,
ms.part_no,
crar1app.INVENTORY_PART_API.GET_DESCRIPTION(ms.contract, ms.part_no) part_desc,
ms.QTY_PER_ASSEMBLY,
ms.PRINT_UNIT uom,
-- ms.ENG_CHG_LEVEL,
crar1app.ENG_PART_REVISION_API.GET_PART_REV(ms.PART_NO, ms.ENG_CHG_LEVEL) rev,
ms.EFF_PHASE_IN_DATE,
ms.EFF_PHASE_OUT_DATE,
ms.BOM_TYPE,
ms.ALTERNATIVE_NO alt
from crar1app.MANUF_STRUCTURE ms
where ms.CONTRACT = nvl('&SITE','10')
and ms.COMPONENT_PART = '&PART_NO'
and ms.EFF_PHASE_IN_DATE <= to_date(nvl('&EFF_DATE',to_char(SYSDATE,'YYYY-MM-DD')),'YYYY-MM-DD')
and (ms.EFF_PHASE_OUT_DATE > to_date(nvl('&EFF_DATE',to_char(SYSDATE,'YYYY-MM-DD')),'YYYY-MM-DD')
or ms.EFF_PHASE_OUT_DATE is null)
and (ms.ALTERNATIVE_NO = 'ML'
or (select 1 from dual where crar1app.MANUF_STRUCT_ALTERNATE_API.GET_OBJSTATE(ms.CONTRACT,ms.PART_NO,ms.ENG_CHG_LEVEL,ms.BOM_TYPE,'ML') in ('Plannable','Buildable')) IS NULL)
I have tried a few things but nothing close to what I am looking for.
Lots of options are available to you in a CONNECT BY query which lets you loop through a hierarchy, eg
SQL> create table parts as
2 select empno part_num, mgr parent_part
3 from scott.emp;
Table created.
SQL>
SQL> select
2 level,
3 part_num,
4 parent_part,
5 sys_connect_by_path(part_num,'-') path,
6 connect_by_isleaf
7 from parts
8 connect by prior part_num = parent_part
9 start with parent_part is null;
LEVEL PART_NUM PARENT_PART PATH CONNECT_BY_ISLEAF
---------- ---------- ----------- ------------------------------ -----------------
1 7839 -7839 0
2 7566 7839 -7839-7566 0
3 7788 7566 -7839-7566-7788 0
4 7876 7788 -7839-7566-7788-7876 1
3 7902 7566 -7839-7566-7902 0
4 7369 7902 -7839-7566-7902-7369 1
2 7698 7839 -7839-7698 0
3 7499 7698 -7839-7698-7499 1
3 7521 7698 -7839-7698-7521 1
3 7654 7698 -7839-7698-7654 1
3 7844 7698 -7839-7698-7844 1
3 7900 7698 -7839-7698-7900 1
2 7782 7839 -7839-7782 0
3 7934 7782 -7839-7782-7934 1

How to select middle 80% rows in Oracle SQL with order by (top % is not working)

I tried top % but that is not working in Oracle SQL. Offset and fetch next are working but I am not able to give percentage.
What will be the best way to fetch middle 80% rows?
Any help will be appreciated, thanks!
Middle 80%? That's between 10 and 90%, then? Let's suppose it is.
Sample data (Scott's EMP table), sorted by salary:
SQL> select ename, job, sal,
2 rank() over (order by sal) rnk
3 from emp order by sal;
ENAME JOB SAL RNK
---------- --------- ---------- ----------
SMITH CLERK 800 1
JAMES CLERK 950 2
ADAMS CLERK 1100 3
WARD SALESMAN 1250 4
MARTIN SALESMAN 1250 4
MILLER CLERK 1300 6
TURNER SALESMAN 1500 7
ALLEN SALESMAN 1600 8
CLARK MANAGER 2450 9
BLAKE MANAGER 2850 10
JONES MANAGER 2975 11
SCOTT ANALYST 3000 12
FORD ANALYST 3000 12
KING PRESIDENT 5000 14
14 rows selected.
CTE ranks employees by their salaries; the final where clause returns rows for those of them who fall into that "middle" 80% (the pct column).
SQL> with temp as
2 (select ename, job, sal,
3 rank() over (order by sal) rnk, -- rank rows by salary
4 count(*) over (order by null) cnt -- total number of rows
5 from emp
6 )
7 select t.*,
8 round(rnk / cnt * 100) pct -- percentage
9 from temp t
10 where round(rnk / cnt * 100) between 10 and 90;
ENAME JOB SAL RNK CNT PCT
---------- --------- ---------- ---------- ---------- ----------
JAMES CLERK 950 2 14 14
ADAMS CLERK 1100 3 14 21
WARD SALESMAN 1250 4 14 29
MARTIN SALESMAN 1250 4 14 29
MILLER CLERK 1300 6 14 43
TURNER SALESMAN 1500 7 14 50
ALLEN SALESMAN 1600 8 14 57
CLARK MANAGER 2450 9 14 64
BLAKE MANAGER 2850 10 14 71
JONES MANAGER 2975 11 14 79
SCOTT ANALYST 3000 12 14 86
FORD ANALYST 3000 12 14 86
12 rows selected.
SQL>

Oracle randomly shuffling entire rows

have a locations table that contains 3 types of records (see below). I only need to SELECT record_type = 'G' rows.
I want to wrap the data in a loop ( ie 5 times) but how can I randomly shuffle the ENTIRE rows, by location_id, so they appear in a different order with each iteration of the loop? Perhaps, sticking the rows into an array and shuffling the array?
Note, I don't want to shuffle the columns, I want the row kept in tact. In the future more locations maybe added.
CREATE TABLE locations AS
SELECT level AS location_id,
'Door ' || level AS location_name,
CASE round(dbms_random.value(1,3))
WHEN 1 THEN 'A'
WHEN 2 THEN 'T'
WHEN 3 THEN 'G'
END AS location_type
FROM dual
CONNECT BY level <= 25;
SELECT * from tour_detail
Order by tour_id, tour_time
TOUR_ID TOUR_TIME LOCATION_ID
1 06212020 00:10:25 2
1 06212020 00:21:05 18
1 06212020 00:30:33 11
1 06212020 00:40:51 17
1 06212020 00:52:13 4
1 06212020 01:01:42 2
1 06212020 01:07:52 11
We have an access control system when you slide your card it records card_num, location_id, access_date in MMDDYYY HH24:MI:SS format.
Some wanted to create a guard tour system. That compares the access_date to the tour_detail detail date. If within a certain criteria say + or - 2 minutes that means the guard was on time, if GT or LT 2 minutes we log an early or late, if GT Or LT 5 minutes or no access_history record we log a no show.
I was asked to throw together some testing data. As you can see the tour_detail records contain MMDDYYYY HH24:MI:SS so I'm thinking about changing the tour_detail records to interval hour to minute but I have little experience working with that.
so they appear in a different order with each iteration of the loop
This is pointless. You are inserting rows into a table. Rows (in a RDBMS table) aren't stored in any particular order.
But, you can sort them when selecting from that table and - in order to do that - you have to use the order by clause.
As of selecting, see if this helps:
SQL> with data as
2 (select empno, ename, job, sal
3 from emp
4 where deptno = 20
5 order by round(dbms_random.value(0, 15))
6 )
7 select column_value, d.*
8 from data d cross join table(cast(multiset(select level from dual
9 connect by level <= 5
10 ) as sys.odcinumberlist));
COLUMN_VALUE EMPNO ENAME JOB SAL
------------ ---------- ---------- --------- ----------
4 7902 FORD ANALYST 3000
4 7788 SCOTT ANALYST 3000
2 7369 SMITH CLERK 1000
4 7369 SMITH CLERK 1000
1 7369 SMITH CLERK 1000
5 7566 JONES MANAGER 2975
1 7876 ADAMS CLERK 1100
1 7788 SCOTT ANALYST 3000
5 7788 SCOTT ANALYST 3000
3 7876 ADAMS CLERK 1100
1 7902 FORD ANALYST 3000
2 7876 ADAMS CLERK 1100
5 7902 FORD ANALYST 3000
3 7902 FORD ANALYST 3000
3 7566 JONES MANAGER 2975
1 7566 JONES MANAGER 2975
3 7788 SCOTT ANALYST 3000
2 7902 FORD ANALYST 3000
4 7566 JONES MANAGER 2975
5 7876 ADAMS CLERK 1100
2 7788 SCOTT ANALYST 3000
3 7369 SMITH CLERK 1000
4 7876 ADAMS CLERK 1100
2 7566 JONES MANAGER 2975
5 7369 SMITH CLERK 1000
25 rows selected.
SQL>
Let's say you have a table with 5 IDs
create table tab as
select rownum id from dual connect by level <= 5;
THis produce a random order output of the rows
select * from tab order by dbms_random.value;
ID
----------
2
4
5
1
3
If you repeat the statment you get a different (random) result
ID
----------
5
4
2
1
3
So simple repaet the query in the loop or make something like this
with data as (
select 1 pass_id, id from tab
union all
select 2 pass_id, id from tab
union all
select 3 pass_id, id from tab )
select * from data
order by 1,dbms_random.value

Loop SQL by month

In Oracle SQL Developer I need to calculate some figures (count for instance) since first day till today monthly. How can I create a loop by changing month in a date field? Now I have a time range on WHERE clause like (date >= '2017-01-01' and date < '2019-01-01'). So what I need is a table that counts for months and inserts results in two columns: month (2019_01, 2019_02, etc) and count
Here's an example which might help you do what need. It is based on Scott's sample schema, its EMP table that contains the HIREDATE column which will be joined to a CTE named months; it utilizes hierarchical query to create "calendar" of 12 months (that's what the CONNECT BY clause does). It will have to be changed in your case, I presume.
Current data:
SQL> select empno, ename, hiredate
2 from emp
3 order by hiredate;
EMPNO ENAME HIREDATE
---------- ---------- ----------
7369 SMITH 17.12.1980
7499 ALLEN 20.02.1981
7521 WARD 22.02.1981
7566 JONES 02.04.1981
7698 BLAKE 01.05.1981
7782 CLARK 09.06.1981
7844 TURNER 08.09.1981
7654 MARTIN 28.09.1981
7839 KING 17.11.1981
7900 JAMES 03.12.1981
7902 FORD 03.12.1981
7934 MILLER 23.01.1982
7788 SCOTT 09.12.1982
7876 ADAMS 12.01.1983
14 rows selected.
SQL>
Query you might need:
SQL> with months as
2 (select add_months(date '1980-12-01', level - 1) mon
3 from dual
4 connect by level <= 12
5 )
6 select to_char(m.mon, 'mm.yyyy') mon,
7 count(e.empno)
8 from months m left join emp e on m.mon = trunc(e.hiredate, 'mm')
9 group by m.mon
10 order by m.mon;
MON COUNT(E.EMPNO)
------- --------------
12.1980 1 --> Smith
01.1981 0
02.1981 2 --> Allen, Ward
03.1981 0
04.1981 1 --> Jones
05.1981 1 --> Blake
06.1981 1 --> Clark
07.1981 0
08.1981 0
09.1981 2 --> Turner, Martin
10.1981 0
11.1981 1 --> King
12 rows selected.
SQL>

Query to sum the previous values

I have the table strucure as in the image.
I need to get the values added to the sum of previous values(shown in the REQUIRED RESULT)
I tried with the following query
SELECT empid,
sum(tot_hours) OVER (PARTITION BY empid ORDER BY empid ) AS tot_hours
FROM empDet
ORDER BY empid
But i get the following result set
But I need the result as in the first picture.
Can anyone help me doing this?
sum(tot_hours) OVER (PARTITION BY empid ORDER BY empid ) AS tot_hours
Your ORDER BY is incorrect. If you want the running SUM on the TOT_HOURS, then you should order by tot_hours.
For example, the below query will calculate the running sum of salary of employees in each department:
SQL> SELECT deptno,
2 sal,
3 SUM(sal) OVER (PARTITION BY deptno ORDER BY sal ) AS tot_sal
4 FROM emp
5 ORDER BY deptno;
DEPTNO SAL TOT_SAL
---------- ---------- ----------
10 1300 1300
10 2450 3750
10 5000 8750
20 800 800
20 1100 1900
20 2975 4875
20 3000 10875
20 3000 10875
30 950 950
30 1250 3450
30 1250 3450
30 1500 4950
30 1600 6550
30 2850 9400
14 rows selected.
SQL>
Update For duplicate values, the running total would be duplicate. To make it unique, use UNBOUNDED PRECEDING clause. For example,
SQL> SELECT empno, deptno,
2 sal,
3 SUM(sal) OVER (PARTITION BY deptno ORDER BY sal ROWS UNBOUNDED PRECEDING) AS tot_sal
4 FROM emp
5 ORDER BY deptno;
EMPNO DEPTNO SAL TOT_SAL
---------- ---------- ---------- ----------
7934 10 1300 1300
10 1300 2600
7782 10 2450 5050
7839 10 5000 10050
7369 20 800 800
7876 20 1100 1900
7566 20 2975 4875
7788 20 3000 7875
7902 20 3000 10875
7900 30 950 950
7521 30 1250 2200
7654 30 1250 3450
7844 30 1500 4950
7499 30 1600 6550
7698 30 2850 9400
15 rows selected.
SQL>