I have several tables that relate to employees and the total amount of sales in dollars and I have to follow these instructions right here:
The company wants to see the total sale amount per sales person (salesman) for all orders. Assume that some online orders do not have any sales person involved. Display the salesman full name and the total sale amount for that employee.
Include also the Total Sale amount for all Orders without involvement of a salesman.
Exclude sales persons whose first name end on y (like Lily or Daisy).
Include only those sales persons who were responsible for more than 4 million dollars in sale.
Sort the result so that bigger sales are shown first.
Note; Do NOT use LIKE operator.
I already have a query statement written in which it shows the employee id instead of the employee name which is what is needed for this query question to be answered properly. In this case the salesman id "," are sales people without names so for ex. sales over the internet therefore no specific name. How do I make it so that I keep the "," but instead show the names of the employee and not their ID Here is the statement:
SELECT NVL(to_char(o.salesman_id), ',') AS "Emp Name", RPAD(to_char(SUM(i.quantity * i.unit_price),'$999,999,999.99'), 16, ' ') AS "Total Sale"
FROM (orders o
INNER JOIN order_items i ON i.order_id = o.order_id)
GROUP BY o.salesman_id
HAVING SUM(i.quantity * i.unit_price) > 4000000.00
ORDER BY 2 DESC;
Heres the output of the query:
Looks like outer join to me.
I don't have your tables so I'll use Scott's emp to illustrate it.
SQL> select empno, ename from emp where deptno = 10;
EMPNO ENAME
---------- ----------
7782 CLARK
7839 KING
7934 MILLER
I'll use those EMPNO values instead of your 62 and 64 (as displayed on the screenshot). your_query CTE represents output you currently have. Then outer join it with table that contains employee names (emp in my case), but leave , for unknown employees:
SQL> with
2 your_query (emp_name, total_sale) as
3 (select ',' , 100 from dual union all
4 select '7782', 200 from dual union all
5 select '7839', 300 from dual
6 )
7 select nvl(e.ename, q.emp_name) ename,
8 q.total_sale
9 from your_query q left join emp e on q.emp_name = to_char(e.empno);
ENAME TOTAL_SALE
---------- ----------
CLARK 200
KING 300
, 100
SQL>
Related
I need to create a report that displays information about what part of the salary in percentage terms each employee receives within the department in which they work, using analytical functions.
SELECT EMPLOYEES.FIRST_NAME, EMPLOYEES.LAST_NAME, EMPLOYEES.DEPARTMENT_ID,
DEPARTMENTS.DEPARTMENT_NAME, EMPLOYEES.SALARY,
(SALARY/SUM(SALARY)) * 100 over (partition by DEPARTMENT_ID) AS "PercentWithinDepartment"
FROM HR.EMPLOYEES
FULL JOIN HR.DEPARTMENTS ON EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
I get an "ORA-00923 FROM keyword not found where expected" error but I think it's not my only mistake within this task.
I cannot provide a code snippet of database but this can be run against the HR sample schema.
My request is to help me figure out mistake to complete this task properly.
The immediate issue is that your over clause is in the wrong place:
(SALARY/SUM(SALARY)) * 100 over (partition by DEPARTMENT_ID)
should be
(SALARY/SUM(SALARY) over (partition by DEPARTMENT_ID)) * 100
But the reference to DEPARTMENT_ID in there is ambiguous as that column is in both tables, so it should be:
(SALARY/SUM(EMPLOYEES.SALARY) over (partition by DEPARTMENTS.DEPARTMENT_ID)) * 100
You might want to consider using table aliases to make the code a bit shorter.
You may also want to round (or truncate/floor) the percentages to, say, one or two decimal places.
I'm not sure you really want a full outer join though - that will include all departments with no employees (e.g. 'Recruiting'). A left outer join looks more appropriate - so 'Kimberley Grant', who is not linked to any department, will still be included.
I have to calculate percent about what part of the salary employee get within all organization and im confused why do i get "not a single-group group function" error here
Because the version you added as a comment has:
SALARY/SUM(EMPLOYEES.SALARY)*100
which is the aggregate form of the function, not the analytic form. As you don't want to aggregate here, you still need an over clause to make it analytical, but that can be empty:
SALARY/SUM(EMPLOYEES.SALARY) over () * 100
And you can of course do both together:
select employees.first_name,
employees.last_name,
employees.department_id,
departments.department_name,
employees.salary,
salary / sum(employees.salary) over (partition by departments.department_id) * 100 as "PercentWithinDepartment",
salary / sum(employees.salary) over () * 100 as "PercentWithinOrganization"
from hr.employees
left join hr.departments on employees.department_id = departments.department_id;
FIRST_NAME LAST_NAME DEPARTMENT_ID DEPARTMENT_NAME SALARY PercentWithinDepartment PercentWithinOrganization
-------------------- ------------------------- ------------- ------------------------------ ---------- ----------------------- -------------------------
Jennifer Whalen 10 Administration 4400 100 .636389933
Michael Hartstein 20 Marketing 13000 68.4210526 1.88024299
Pat Fay 20 Marketing 6000 31.5789474 .867804455
...
Kimberely Grant 7000 100 1.01243853
You might also want to add an order by clause...
I don't have HR schema (but I have Scott) so - here's how. I'm using SQL*Plus' formatting capabilities to make it look prettier.
SQL> set numformat 999g990d00
SQL> break on deptno on dname
SQL> compute sum of pct_sal on deptno
SQL>
SQL> select e.deptno, d.dname, e.ename, e.sal,
2 sum(e.sal) over (partition by e.deptno) dept_sal,
3 --
4 round((e.sal / sum(e.sal) over (partition by e.deptno)) * 100, 2) pct_sal
5 from emp e join dept d on d.deptno = e.deptno
6 order by e.deptno, e.ename;
Result:
DEPTNO DNAME ENAME SAL DEPT_SAL PCT_SAL
----------- -------------- ---------- ----------- ----------- -----------
10,00 ACCOUNTING CLARK 2.450,00 8.750,00 28,00
KING 5.000,00 8.750,00 57,14
MILLER 1.300,00 8.750,00 14,86
*********** ************** -----------
sum 100,00
20,00 RESEARCH ADAMS 1.100,00 10.915,00 10,08
FORD 3.000,00 10.915,00 27,49
JONES 2.975,00 10.915,00 27,26
SCOTT 3.000,00 10.915,00 27,49
SMITH 840,00 10.915,00 7,70
*********** ************** -----------
sum 100,02
30,00 SALES ALLEN 1.600,00 9.400,00 17,02
BLAKE 2.850,00 9.400,00 30,32
JAMES 950,00 9.400,00 10,11
MARTIN 1.250,00 9.400,00 13,30
TURNER 1.500,00 9.400,00 15,96
WARD 1.250,00 9.400,00 13,30
*********** ************** -----------
sum 100,01
14 rows selected.
SQL>
(Sum isn't exactly 100% because of rounding.)
I might be mistaking, but it seems to me that all you need is to use ratio_to_report analytic function. Try this one please
SELECT E.FIRST_NAME, E.LAST_NAME, E.DEPARTMENT_ID,
D.DEPARTMENT_NAME, E.SALARY,
100 * ratio_to_report(e.salary) over (partition by d.department_id)
FROM HR.EMPLOYEES e
JOIN hr.departments d
on e.department_id = d.department_id
order by d.department_id;
I have created a query in PLSQL to search customer records from the tables. But still has some limitations. In the &CUSTOMER_ID Search field If I Put IN% OR DL100056* It will search All the details which start with IN or giving the exact DL details as output. I want to search multiple values like in the customer Id field I wanna search DL100056,DL1000365,DL1000652 likewise. But my current query not giving multiple outputs.
SELECT
DISTINCT CUSINFO.CUSTOMER_ID AS DEALER_CODE,
CUSINFO.name AS DEALER_NAME,
CUSINFO.CORPORATE_FORM AS FORM_OF_BUSINESS,
C.corporate_form_desc AS FORM_OF_BUSINESS_DESCRIPTION,
ifsapp.Customer_Info_Address_Api.Get_Address1(CUSINFO.customer_id,1) "ADDRESS 1",
ifsapp.Customer_Info_Address_Api.Get_Address2(CUSINFO.customer_id,1) "ADDRESS 2",
CREDTIN.credit_limit AS CREDIT_LIMIT,
CREDTIN.allowed_due_amount AS ALLOWED_DUE_AMOUNT,
CREDTIN.allowed_due_days AS ALLOWED_DUE_DAYS,
ORDERC.salesman_code AS SALESMAN_CODE,
BDA.name AS SALESMAN_NAME,
ORDERC.discount AS DISCOUNT,
(select LISTAGG(c.value,' / ') within group (order by c.value) from ifsapp.COMM_METHOD c
where c.method_id_db='PHONE' and c.identity=CUSINFO.customer_id ) "Phone",
(select LISTAGG(c.value,' / ') within group (order by c.value) from ifsapp.COMM_METHOD c
where c.method_id_db='MOBILE' and c.identity=CUSINFO.customer_id) "Mobile",
(select LISTAGG(c.value,' / ') within group (order by c.value) from ifsapp.COMM_METHOD c
where c.method_id_db='FAX' and c.identity=CUSINFO.customer_id) "Fax",
CUSINFO.identifier_reference as IDENTIFIER_REFERENCE
FROM
ifsapp.CUSTOMER_INFO CUSINFO
LEFT JOIN
ifsapp.CUSTOMER_INFO_ADDRESS CUSADD ON CUSINFO.customer_id=CUSADD.customer_id
LEFT JOIN
ifsapp.CUSTOMER_INFO_CONTACT CUSCONT ON CUSINFO.customer_id=CUSCONT.customer_id
LEFT JOIN
ifsapp.CUSTOMER_CREDIT_INFO_CUST CREDTIN ON CUSINFO.customer_id=CREDTIN.identity
LEFT JOIN
ifsapp.CUST_ORD_CUSTOMER_ENT ORDERC ON CUSINFO.customer_id=ORDERC.customer_id
LEFT JOIN
ifsapp.Comm_Method COMM ON CUSINFO.customer_id=COMM.customer_id
LEFT JOIN
ifsapp.CORPORATE_FORM C ON CUSINFO.corporate_form=C.CORPORATE_FORM
LEFT JOIN
ifsapp.SALES_PART_SALESMAN_LOV BDA ON ORDERC.salesman_code=BDA.salesman_code
WHERE CUSINFO.CUSTOMER_ID LIKE '&CUSTOMER_ID'
It can't work that way; one option is to use such a query (I don't have your tables so I used Scott's DEPT): it splits parameter values (separated by comma) into rows and uses them as a subquery. So, what you need is lines #3 - 5 (adjusted to your table / column name).
SQL> select deptno, dname, loc
2 from dept
3 where deptno in (select to_number(regexp_substr('&&par_deptno', '[^,]+', 1, level))
4 from dual
5 connect by level <= regexp_count('&&par_deptno', ',') + 1
6 )
7 order by deptno;
Enter value for par_deptno: 10
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
SQL> undefine par_deptno
SQL> /
Enter value for par_deptno: 20,40
DEPTNO DNAME LOC
---------- -------------- -------------
20 RESEARCH DALLAS
40 OPERATIONS BOSTON
SQL>
please format your sql better, it is very difficult to understand the logic behind the query if its is unreadable.
You could try by using LIKE 'DL%'. This will look for all strings starting with DL. Another solution, if the code doesn't always start with 'DL', is using a substring on the code to get the first few characters, then adding '%'
hope this helped
My query looks like below with the foll. output
Current Output
Role Cases prepped % Completed
State Member 1 10 5 50%
State Member 2 10 7 70%
State President 10 2 20%
Summary 30 14 46.6%
Output Expected
Role Cases prepped % Completed
State President 10 2 20%
State Member 1 10 5 50%
State Member 2 10 7 70%
Summary 30 14 46%
Roles table
id name
30 State President
40 State Member
This is my query,
SELECT COALESCE(ROLE, 'Summary') ROLE,
count(*) AS cases,
SUM(CASE WHEN PREPARED = 'Y' THEN 1
ELSE 0
END) AS prepped,
round(avg(case when prepared = 'Y' then 100 else 0 end),2)||'%' as % Completed
FROM
(SELECT CASE
WHEN r.id = 30 THEN r.name
ELSE r.name || ' ' || u.case_member_id
END AS ROLE,
bi.prepared
FROM cases c
LEFT JOIN case_inventory ci ON ci.case_id = c.id
AND c.id = ci.case_id
AND c.delete_date IS NULL
AND ci.case_id =40
Left JOIN users u ON ci.assigned_to = u.id
Left JOIN ROLES r ON u.role_id = r.id
Left JOIN user_cases_map uc ON c.id = uc.case_id
AND uc.id = 1572919346)
GROUP BY ROLLUP (ROLE);
I now want to order the rows with respect to the role. The 1st record should be the State president and then followed by state memebr 1. state member 2. and so on. I tried to have an order by in the inner clause but it did not help. It doesnt have any effect. Adding in the outer select also doesnt change anything. Any help highly appreciated. Thank you.
You could do something like this. I don't have your input data, so I used SCOTT.EMP instead.
Notice a few things. I grouped by JOB, and I used GROUPING(JOB) both in SELECT (to add the label TOTAL for the summary row) and in ORDER BY. Since I reuse the column name JOB in SELECT (for the output column), in ORDER BY I must be careful to qualify the column name JOB (to make it clear I am referring to the input table column, not to the column in SELECT - which would be the default if column names in ORDER BY were not qualified). The need to qualify column names in ORDER BY, then, forced me to alias the table in the FROM clause (otherwise I would have had to carry the full table name everywhere).
Using the GROUPING function in SELECT (rather than NVL) is particularly important if JOB can be null. You don't want the group for null job to be labeled TOTAL - you only want that for the rollup row. This point confuses even a lot of very advanced programmers.
I show how you can "manually" decide the order: PRESIDENT first, then MANAGER, and then all other jobs (ordered alphabetically). If you have the order of priority saved somewhere, for example in a table, you can join to that table and use the ordering column instead of the "manual" CASE expression in my query.
select case grouping(job) when 0 then job else 'TOTAL' end as job
, sum(sal) as total_salary
from scott.emp e
group by rollup(job)
order by grouping(e.job) -- to get the total in the last row
, case e.job when 'PRESIDENT' then 1 when 'MANAGER' then 2 end
, e.job
;
JOB TOTAL_SALARY
--------- ------------
PRESIDENT 5000
MANAGER 8275
ANALYST 6000
CLERK 4150
SALESMAN 5600
TOTAL 29025
I have the following sql query that I m not able to process since this afternoon.
I have seen lot of threads about this issue but I m not getting it right, I believe that I m miss understanding this one topic that is shaming my day.
SELECT
cor.c_order_id, cor.totallines,cor.documentno,cbp.name
FROM
c_orderline col
LEFT JOIN c_order cor ON cor.c_order_id = col.c_order_id
LEFT join c_bpartner cbp on cbp.c_bpartner_id = cor.c_bpartner_id
WHERE
cor.issotrx = 'Y'
and cor.docstatus not in ('DR','IP')
AND cor.salesrep_id = 1037317
and col.qtyordered <> 0
and cor.dateordered between SYSDATE - 30 and SYSDATE + 30
AND col.c_orderline_id NOT IN (
SELECT
cil.c_orderline_id
FROM
c_invoiceline cil
WHERE
cil.c_orderline_id IS NOT NULL
)
group by cor.c_order_id, cor.documentno
order by cor.c_order_id, cor.documentno
What I m doing wrong ??
A simplified example, based on Scott's EMP table.
This is what you did:
SQL> select deptno, job
2 from emp
3 group by deptno;
select deptno, job
*
ERROR at line 1:
ORA-00979: not a GROUP BY expression
As you can see, Oracle marked the culprit - the JOB column.
Therefore, if you want to use a GROUP BY clause (which is correct, but - you'd get the same result using DISTINCT; these two should not be used together. GROUP BY is usually used with aggregates such as min, max, avg and such), then put all columns into the GROUP BY:
SQL> select deptno, job
2 from emp
3 group by deptno, job;
DEPTNO JOB
---------- ---------
20 CLERK
30 SALESMAN
20 MANAGER
30 CLERK
10 PRESIDENT
30 MANAGER
10 CLERK
10 MANAGER
20 ANALYST
9 rows selected.
Or, as I said - using DISTINCT:
SQL> select distinct deptno, job
2 from emp;
DEPTNO JOB
---------- ---------
20 CLERK
30 SALESMAN
20 MANAGER
30 CLERK
10 PRESIDENT
30 MANAGER
10 CLERK
10 MANAGER
20 ANALYST
9 rows selected.
SQL>
group by is used for aggregate functions like sum(), max(), min() etc. What value are you calculating in groups?
remove the group by and your query looks good besides that
To correct the error this query is producing you need to
group by cor.c_order_id, cor.totallines,cor.documentno,cbp.name
Group by all the columns that are in the select statement and are not part of aggregate function.
What else is a problem?
Here is the DEMO
In this demo you will see that the query works(with the correction I have suggested), but as I asked, what else is a problem to you. Do elaborate so we can help. Cheers!
I have a preexisting table (MYORIGTABLE) in SQL and I would like to add a column which is a random sample from a column in another table (RANDNUM10). The other table is a 1 column sample of random numbers I've predetermined and would like to sample from; column name is RANDVAL. I've tried the following
select
(select randval from
(SELECT randval
FROM RANDNUM10
ORDER BY dbms_random.value)
where rownum=1) as mynum
from MYORIGTABLE
But unfortunately this just gives me the same random number which was sampled from RANDNUM10 repeated for the entire length of MYORIGTABLE. How can I perform the sampling so that every line of MYORIGTABLE gets a newly generated sample?
Thanks,
Christie
This is your query:
select (select randval
from (SELECT randval
FROM RANDNUM10
ORDER BY dbms_random.value
)
where rownum = 1
) as mynum
from MYORIGTABLE;
A common problem with database optimizers -- in this situation -- that that it optimizes away the multiple calls to the subquery. It ends up replacing the subquery with one call.
One way around this is to use a correlated subquery. For this, I would recommend removing one level of nesting:
select (select max(randval) keep (dense_rank first order by dbms_rnadom.value)
from randnum10
where myorigtable.id is not null -- or whatever
) as mynum
from MYORIGTABLE;
I think this will work so the subquery is called 10 times, although a more complex correlation clause might be needed.
Something like this, perhaps?
Test case:
SQL> create table myorigtable (id number);
Table created.
SQL> create table randnum10 (randval number);
Table created.
SQL> insert into myorigtable select level from dual connect by level <= 10;
10 rows created.
SQL> insert into randnum10 select round(dbms_random.value(1, 100)) from dual connect by level <= 10;
10 rows created.
SQL> alter table myorigtable add randval number;
Table altered.
Update:
SQL> update myorigtable m set
2 m.randval = (select x.randval
3 from (select randval, row_number() over (order by randval) rn
4 from randnum10
5 ) x
6 where x.rn = m.id
7 );
10 rows updated.
SQL> select * from myorigtable;
ID RANDVAL
---------- ----------
1 5
2 18
3 29
4 31
5 31
6 62
7 66
8 87
9 94
10 98
10 rows selected.
SQL>
Here is one approach. I assume your RANDNUM10 table has many rows (more rows than MYORIGTABLE), you want to sample the random numbers without replacement (select DISTINCT random values from RANDNUM10) - and therefore the random numbers table has enough rows - at least as many as MYORIGTABLE - and you need to add the column to the result set of a SELECT query, rather than to the stored table. (If you need to store the result in an additional column, then you must first add the column if it doesn't already exist, and use the SELECT query below in an UPDATE statement.)
The strategy is simple: associate arbitrary row numbers to the smaller table (with values 1, 2, ..., N where N is the row count), and associate random row numbers to the rows in the random numbers table. Both can be done with ROW_NUMBER()... - the first one can be ordered by NULL since all the randomness we need will come from the other table's row numbers anyway. Then JOIN the two tables on the row number thus added to both tables.
For illustration I created MYORIGTABLE as a copy of SCOTT.EMP, selecting only a few columns from that table. Here's what the query looks like, and the output using that as MYORIGTABLE.
Setup:
create table myorigtable as select empno, ename, job, deptno from scott.emp;
create table randnum10 ( randval ) as
select dbms_random.value() from dual connect by level <= 1000;
Query and output:
select a.empno, a.ename, a.job, a.deptno, b.randval
from (
select empno, ename, job, deptno, row_number() over (order by null) as rn
from myorigtable
) a
inner join
(
select randval, row_number() over (order by dbms_random.value()) as rn
from randnum10
) b
on a.rn = b.rn
;
EMPNO ENAME JOB DEPTNO RANDVAL
----- ---------- --------- ------- ----------
7369 SMITH CLERK 20 .991998901
7499 ALLEN SALESMAN 30 .448133242
7521 WARD SALESMAN 30 .136242235
7566 JONES MANAGER 20 .443347421
7654 MARTIN SALESMAN 30 .836931008
7698 BLAKE MANAGER 30 .361437867
7782 CLARK MANAGER 10 .433786615
7788 SCOTT ANALYST 20 .285173363
7839 KING PRESIDENT 10 .063840361
7844 TURNER SALESMAN 30 .825888729
7876 ADAMS CLERK 20 .818415041
7900 JAMES CLERK 30 .150180080
7902 FORD ANALYST 20 .442389099
7934 MILLER CLERK 10 .086995415