How to not return a value of 0 when data does not exist in table, SQLPLUS - sql

I have two tables, table A and B, and I am counting the number of occurences of a data type from table A,
ENAME SALES
---------- ----------
Wilson 0
Fox 1
Wang 2
Green 1
Fox 0
Phillips 2
Loftus 0
Smith 2
Smith 3
Wolf 2
Jones 1
Liu 0
Where when the sales count is 0, the ename does not exist in table B at all.
How would I go about removing the 0 count? I'm trying to get the return to look like this:
ENAME SALES
---------- ----------
Wilson
Fox 1
Wang 2
Green 1
Fox
Phillips 2
Loftus
Smith 2
Smith 3
Wolf 2
Jones 1
Liu

One method uses case, another nullif():
select ename, nullif(sales, 0) as sales
from t;
If this is an aggregation query:
select ename, nullif(count(*), 0) as sales
from t
group by ename;

Related

self join query

emp_id
emp
level
manager_id
manager
100
sam
1
100
sam
200
jack
2
100
sam
300
jill
2
100
sam
400
claire
3
200
jack
500
reed
3
300
jill
600
derrick
4
400
claire
700
bill
4
500
reed
I have a table with employees and their respective managers in column 'emp' and 'manager' respectively. The numbers in the 'level' column represent different levels within an organisation for the employees in column 'emp'.
How do i write a query to get the manager name from the previous level as an entry in step and so forth.
emp_id
emp
level
manager_id
manager
l1
l2
l3
l4
100
sam
1
100
sam
NULL
NULL
NULL
NULL
200
jack
2
100
sam
sam
NULL
NULL
NULL
300
jill
2
100
sam
sam
NULL
NULL
NULL
400
claire
3
200
jack
sam
jack
NULL
NULL
500
reed
3
300
jill
sam
jill
NULL
NULL
600
derrick
4
400
claire
sam
jack
claire
NULL
700
bill
4
500
reed
sam
jill
reed
NULL
You seem to be looking for a recursive query. One that keeps joining on the next level until there are no more levels to join to.
That can be used to get all of an employees managers, each manager as a new row.
You then want to pivot those rows in to columns. Note, however, that SQL is statically and strongly typed, which means that if you want a pivoted view, you have to choose in advance how many columns you're going to have.
For example...
WITH
recurse_upwards AS
(
SELECT
emp.emp_id,
emp.emp AS emp_name,
0 AS level,
mgr.emp_id AS manager_id,
mgr.emp AS manager_name,
mgr.manager_id AS next_manager_id
FROM
example AS emp
LEFT JOIN
example AS mgr
ON mgr.emp_id = emp.manager_id
UNION ALL
SELECT
emp.emp_id,
emp.emp_name,
emp.level + 1,
mgr.emp_id,
mgr.emp,
mgr.manager_id
FROM
recurse_upwards AS emp
INNER JOIN
example AS mgr
ON mgr.emp_id = emp.next_manager_id
)
SELECT
emp_id,
emp_name,
MAX(CASE WHEN level = 0 THEN manager_id END) AS manager_id,
MAX(CASE WHEN level = 0 THEN manager_name END) AS manager_name,
MAX(CASE WHEN level = 1 THEN manager_name END) AS manager_1_name,
MAX(CASE WHEN level = 2 THEN manager_name END) AS manager_2_name,
MAX(CASE WHEN level = 3 THEN manager_name END) AS manager_3_name,
MAX(CASE WHEN level = 4 THEN manager_name END) AS manager_4_name
FROM
recurse_upwards
GROUP BY
emp_id,
emp_name
ORDER BY
emp_id
Demo : https://dbfiddle.uk/Tj7rZ5bT

How do I display rows from a max count?

I want to return all the data, from max count query with hospital that has most number of patients. What I seem to be getting when I try to nest queries is display of all rows of hospital data. I've tried to look at similar questions in stack overflow and other sites it seems to be a simple query to do but i am not getting it.
select max(highest_hospital) as max_hospital
from (select count(hospital) as highest_hospital
from doctor
group by hospital)
highest_hospital
-------------
3
Doc ID Doctor Patient Hospital Medicine Cost
------ ------- ------ --------- ------ --------
1 Jim Bob Patient1 Town 1 Medicine 1 4000
2 Janice Smith Patient2 Town 2 Medicine 3 3000
3 Harold Brown Patient3 Town 2 Medicine 5 2000
4 Larry Owens Patient4 Town 2 Medicine 6 3000
5 Sally Brown Patient5 Town 3 Medicine 7 4000
6 Bob Jim Patient6 Town 4 Medicine 8 6000
Outcome should be return of 3 rows
Doc ID Doctor Patient Hospital Medicine Cost
------ ------- ------ --------- ------ --------
2 Janice Smith Patient2 Town 2 Medicine 3 3000
3 Harold Brown Patient3 Town 2 Medicine 5 2000
4 Larry Owens Patient4 Town 2 Medicine 6 3000
You can use window functions:
select d.*
from (select d.*, max(hospital_count) over () as max_hospital_count
from (select d.*, count(*) over (partition by hospital) as hospital_count
from doctor d
) d
) d
where hospital_count = max_hospital_count;
Edit:
Using GROUP BY is a pain. If you are only looking for a single hospital (even when there are ties), then in Oracle 12C you can do:
select d.*
from doctor d
where d.hospital = (select d2.hospital
from doctor d2
group by d2.hospital
order by count(*) desc
fetch first 1 row only
);
You can do this in earlier versions of Oracle using an additional subquery.

Find duplicate batches based on multiple columns

I have a table that contains a series of related records (batches). Each batch has a unique id and can contain customer payments. I want to find if a batch is duplicate even if it is submitted on different days.
A batch can have 1 or more records. Here is sample data set:
BatchId InputAmount CustomerName BatchDate
------- ----------- ------------ ----------
182944 $475.00 Barry Smith 16-Mar-2019
182944 $260.00 John Smith 16-Mar-2019
182944 $265.00 Jane Smith 16-Mar-2019
182944 $400.00 Sara Smith 16-Mar-2019
182944 $175.00 Andy Smith 16-Mar-2019
182945 $475.00 Barry Smith 16-Mar-2019
182945 $260.00 John Smith 16-Mar-2019
182945 $265.00 Jane Smith 16-Mar-2019
182945 $400.00 Sara Smith 16-Mar-2019
182945 $175.00 Andy Smith 16-Mar-2019
183194 $100.00 Paul Green 21-Mar-2019
183195 $100.00 Nancy Green 21-Mar-2019
183197 $150.00 John Brown 20-Mar-2019
183197 $210.00 Sarah Brown 20-Mar-2019
183198 $150.00 John Brown 21-Mar-2019
183198 $210.00 Sarah Brown 21-Mar-2019
183200 $125.00 John Doe 20-Mar-2019
183200 $110.00 Sarah Doe 20-Mar-2019
183202 $125.00 John Doe 21-Mar-2019
183202 $110.00 Sarah Doe 21-Mar-2019
183202 $115.00 Paul Rudd 21-Mar-2019
Batches (182944, 182945) and (183197,183198) are duplicate while the other batches are not.
I thought maybe I could create a summary table with counts and sums and get close but I'm having trouble finding the true duplicates by including the names as well.
DECLARE #Summaries TABLE(
BatchId INT,
BatchDate DATETIME,
BatchCount INT,
BatchAmount MONEY)
-- Summarize the Data so we can look for duplicates
INSERT INTO #Summaries
SELECT a.BatchId, a.BatchDate, COUNT(*) AS RecordCount, SUM(a.InputAmount) AS BatchAmount
FROM Batches a
WHERE a.BatchDate BETWEEN '20190316' and '20190321'
GROUP BY a.BatchId, a.BatchDate
ORDER BY a.BatchId DESC
-- find the potential duplicate batches based on the Counts and Sums
SELECT A.* FROM #Summaries A
INNER JOIN (SELECT BatchCount, BatchAmount, BatchDate FROM #Summaries
GROUP BY BatchCount, BatchAmount, BatchDate
HAVING COUNT(*) > 1) B
ON A.BatchCount = B.BatchCount
AND A.BatchAmount = B.BatchAmount
WHERE DATEDIFF(DAY, a.BatchDate, b.BatchDate) BETWEEN -1 AND 1
Thank you for the help. I'm using a SQL Server 2012 database.
you can try like below
with cte as
(select BatchId from table_name
group by BatchId
having count(*)>1
) select * from table_name a where a.BatchId in (select BatchId from cte)

How do I transpose multiple rows to columns in SQL

My first time reading a question on here.
I am working at a university and I have a table of student IDs and their supervisors, some of the students have one supervisor and some have two or three depending on their subject.
The table looks like this
ID Supervisor
1 John Doe
2 Peter Jones
2 Sarah Jones
3 Peter Jones
3 Sarah Jones
4 Stephen Davies
4 Peter Jones
4 Sarah Jones
5 John Doe
I want to create a view that turns that into this:
ID Supervisor 1 Supervisor 2 Supervisor 3
1 John Doe
2 Peter Jones Sarah Jones
3 Peter Jones Sarah Jones
4 Stephen Davies Peter Jones Sarah Jones
5 John Doe
I have looked at PIVOT functions, but don't think it matches my needs.
Any help is greatly appreciated.
PIVOT was the right clue, it only needs a little 'extra' :)
DECLARE #tt TABLE (ID INT,Supervisor VARCHAR(128));
INSERT INTO #tt(ID,Supervisor)
VALUES
(1,'John Doe'),
(2,'Peter Jones'),
(2,'Sarah Jones'),
(3,'Peter Jones'),
(3,'Sarah Jones'),
(4,'Stephen Davies'),
(4,'Peter Jones'),
(4,'Sarah Jones'),
(5,'John Doe');
SELECT
*
FROM
(
SELECT
ID,
'Supervisor ' + CAST(ROW_NUMBER() OVER(PARTITION BY ID ORDER BY Supervisor) AS VARCHAR(128)) AS supervisor_id,
Supervisor
FROM
#tt
) AS tt
PIVOT(
MAX(Supervisor) FOR
supervisor_id IN ([Supervisor 1],[Supervisor 2],[Supervisor 3])
) AS piv;
Result:
ID Supervisor 1 Supervisor 2 Supervisor 3
1 John Doe NULL NULL
2 Peter Jones Sarah Jones NULL
3 Peter Jones Sarah Jones NULL
4 Peter Jones Sarah Jones Stephen Davies
5 John Doe NULL NULL
You will notice that the assignment to Supervisor X is done by ordering by the Supervisor-VARCHAR. If you want the ordering done differently, you might want to include an [Ordering] column; then change to ROW_NUMBER() OVER(PARTITION BY ID ORDER BY [Ordering]). Eg an [Ordering] column could be an INT IDENTITY(1,1). I'll leave that as an excercise to you if that's what's really needed.

get extra rows for each group where date doesn't exist

I've been playing with this for days, and can't seem to come up with something. I have this query:
select
v.emp_name as Name
,MONTH(v.YearMonth) as m
,v.SalesTotal as Amount
from SalesTotals
Which gives me these results:
Name m Amount
Smith 1 123.50
Smith 2 40.21
Smith 3 444.21
Smith 4 23.21
Jones 1 121.00
Jones 2 499.00
Jones 3 23.23
Jones 4 41.82
etc....
What I need to do is use a JOIN or something, so that I get a NULL value for each month (1-12), for each name:
Name m Amount
Smith 1 123.50
Smith 2 40.21
Smith 3 444.21
Smith 4 23.21
Smith 5 NULL
Smith 6 NULL
Smith ... NULL
Smith 12 NULL
Jones 1 121.00
Jones 2 499.00
Jones 3 23.23
Jones 4 41.82
Jones 5 NULL
Jones ... NULL
Jones 12 NULL
etc....
I have a "Numbers" table, and have tried doing:
select
v.emp_name as Name
,MONTH(v.YearMonth) as m
,v.SalesTotal as Amount
from SalesTotals
FULL JOIN Number n on n.Number = MONTH(v.YearMonth) and n in(1,2,3,4,5,6,7,8,9,10,11,12)
But that only gives me 6 additional NULL rows, where what I want is actually 6 NULL rows for each group of names. I've tried using Group By, but not sure how to use it in a JOIN statement like that, and not even sure if that's the correct route to take.
Any advice or direction is much appreciated!
Here's one way to do it:
select
s.emp_name as Name
,s.Number as m
,st.salestotal as Amount
from (
select distinct emp_name, number
from salestotals, numbers
where number between 1 and 12) s left join salestotals st on
s.emp_name = st.emp_name and s.number = month(st.yearmonth)
Condensed SQL Fiddle
You could do:
SELECT EN.emp_name Name,
N.Number M,
ST.SalesTotal Amount
FROM ( SELECT Number
FROM NumberTable
WHERE Number BETWEEN 1 AND 12) N
CROSS JOIN (SELECT DISTINCT emp_name
FROM SalesTotals) EN
LEFT JOIN SalesTotals ST
ON N.Number = MONTH(ST.YearMonth)
AND EN.emp_name = ST.emp_name