I want SQLplus count(*) to return zero-values - sql

I'm using Oracle 10.2 and have the following query:
select h.company, count(*)
from history h
where h.status = '2'
and h.substatus = '0'
and h.timestamp > '2012-01-01'
and h.timestamp < '2012-02-01'
group by h.company
having (h.company = 'AAA')
or (h.company = 'BBB')
or (h.company = 'CCC')
order by h.company
This will count the amount of times any customer from the companies AAA, BBB or CCC has reached a specific point (status = 2).
Presume no (zero) customers from BBB did so, the result will come back with 2 rows of count(AAA and CCC).
What I want: I wish for the query to return me rows for all 3 companies, even tho the count is zero.
Sorry for the odd layout of the query. It's made to work with MS Excel as well.
Edit: Sorry.. Too little caffeine. changed "Customer" in the later half of the query to "Company".
Clarification: A customer is made unique by combining "h.company" and "h.customer" (or by using the same method in customer-table (customer c), like "c.company" and "c.customer"
Edit 2: Updated code.
select c.company, count(*)
from companyregister c
left join history h on h.company = c.company
where h.status = '2'
and h.substatus = '0'
and h.timestamp > '2012-01-01'
and h.timestamp < '2012-02-01'
group by c.company
having (c.company = 'AAA')
or (c.company = 'BBB')
or (c.company = 'CCC')
order by c.company
Both sets of code from above will yield two rows as follows:
AAA 630
CCC 3020
I would like to have BBB represented, but since they have zero rows in history, they don't show.

Make a left join on the customer table. I don't know what you have named it, but like this:
select c.company, count(h.customer)
from customer as c
left join history as h on h.customer = c.customer
...
Another alternative is to use a condition when counting. I'm not sure if there is any other condition that you need along with the status, but something liket this:
select company, sum(case status when '2' then 1 else 0 end)
from history
where substatus = '0'
and timestamp > '2012-01-01'
and timestamp < '2012-02-01'
and customer in ('AAA', 'BBB', 'CCC')
group by customer
order by customer

Related

TSQL - Multiple Values in INSERT (because of joins)

Im trying to insert a data from one database to another. This is what i have so far, on the select side:
USE [db2]
SELECT
sP.pers_FirstName
,sp.pers_LastName
,sPH.Phon_Number
,CASE WHEN LEFT(sPH.Phon_Number, 2) = '04' THEN sPH.Phon_number ELSE NULL END
,CASE WHEN sp.pers_gender = 1 THEN 'M' WHEN sp.pers_gender = 2 THEN 'F' ELSE 'U' END
,CASE
WHEN sP.pers_salutation = '10' THEN 8
WHEN sp.pers_salutation = '6' THEN 2
WHEN sp.pers_salutation = '7' THEN 1
WHEN sp.pers_salutation = '8' THEN 4
WHEN sp.pers_salutation = '9' THEN 5
WHEN sp.pers_salutation = 'APROF' THEN 6
WHEN sp.pers_salutation = 'Ms.' THEN 4
WHEN sp.pers_salutation = 'PROF' THEN 6
END
,sp.pers_dob
,sp.pers_CreatedDate
,sp.pers_UpdatedDate
,'Candidate'
,1
,e.Emai_EmailAddress
,sP.pers_personID
FROM [db1].dbo.person sP
LEFT JOIN [db1].dbo.PhoneLink sPL ON sp.pers_personID = sPL.PLink_recordID
LEFT JOIN [db1].dbo.Phone sPH ON sPL.PLink_PhoneId = sPH.Phon_PhoneID
LEFT JOIN [db1].dbo.EmailLink eL ON sP.pers_personID = eL.ELink_RecordID
LEFT JOIN [db1].dbo.Email e ON eL.Elink_EmailID = e.Emai_EmailID
WHERE
(
sP.pers_employedby NOT IN (
'Aspen'
,'ACH'
)
)
OR
(
sP.pers_employedby IN (
'Aspen'
,'ACH'
)
AND sP.pers_personID NOT IN (
SELECT c.oppo_PrimaryPersonID FROM [SageCRM].dbo.Opportunity c
WHERE (c.oppo_contractcompleted <= '2016-01-01' OR c.oppo_contractterminated <= '2016-01-01') and c.Oppo_Deleted is null)
AND
sp.pers_isanemployee != 'ECHO'
AND sP.pers_personID IN (
SELECT c.oppo_PrimaryPersonID FROM [SageCRM].dbo.Opportunity c
WHERE c.oppo_Status != 'In Progress' OR c.oppo_Status = 'Completed')
AND sP.pers_dod IS NULL
AND sP.pers_FirstName NOT LIKE '%test%'
AND sP.pers_LastName NOT LIKE '%test%'
AND sp.pers_isanemployee != 'SalesContact'
)
Due to the fact that each person record can have multiple phone numbers linked to them, i end up with multiple records for each person, which obviously wont work as i will end up with duplicates when i actually insert the data.
The problem is, that i need to have all of the phone numbers for each record, just displayed in a different field (home phone, work phone, mobile phone).
Any Ideas, other than doing this in a separate insert statement for each phone / email link?
-------- EDIT: -----------------------------------------------------------------
Ok so, my bad for not giving you enough information. Both of your answers were good answers so thanks for that (#Horaciux, #John Wu).
However, there is no phoneType column, just a phone number. That being said, since every mobile starts with 04 and every home phone with anything else, i can pretty easily distinguish between the two phone types.
There are duplicates in the phone table though, so i will have to delete these, most likely via CTE, shouldn't be too hard.
So, i will end up with something like this for the two phone numbers:
SELECT (phon_number FROM phone p INNER JOIN PhoneLink p1 on p1.PhoneLinkID = p.PhoneLink WHERE LEFT(p.Phon_Number, 2) = '04')
SELECT (phon_number FROM phone p INNER JOIN PhoneLink p1 on p1.PhoneLinkID = p.PhoneLink WHERE LEFT(p.Phon_Number, 2) != '04')
My duplicate removal will be something like this:
WITH CTE AS
(
SELECT phon_linkID, phon_phonNumber, ROW_NUMBER() OVER (PARTITION BY phon_phonNumber ORDER BY phon_linkID) AS RN
FROM phone
)
DELETE FROM CTE WHERE RN<>1
Two easy steps.
Get rid of the joins to the phone number table.
Lookup the phone numbers per record by using a subquery in the select clause, one for each type of phone. Example
SELECT sP.pers_FirstName,
sP.pers_LastName,
(SELECT Phon_Number FROM Phone p JOIN PhoneLink pl ON pl.PhoneLinkID = p.PhoneLinkID WHERE pl.Person_ID = sP.pers_personID AND pl.Type = 'WORK') WorkPhone,
(SELECT Phon_Number FROM Phone p JOIN PhoneLink pl ON pl.PhoneLinkID = p.PhoneLinkID WHERE pl.Person_ID = sP.pers_personID AND pl.Type = 'HOME') HomePhone
FROM person
Without knowing your table's structure, I'll do some example.
select person.id,
max(case when phone.type='home' then phone.vlaue else 0 end) 'home',
max(case when phone.type='work' then phone.vlaue else 0 end) 'work'
from person,phone where...
group by person.id
Then use this query to join all other tables needed

Filtering data only if criteria is met

I am trying to configure this query that I have but I can't think of a way to do it. My query is below:
select per.Forenames, per.Surname, p.Identifier2
from patient p
join Person per on per.PersonID = p.PersonID
where not exists (select 1
from Episode e
where e.PatientID = p.PatientID and
e.EpisodeTypeID in ('FCB9EAA0-C814-413E-A5FC-48547EF973B7',
'E422A8FA-839B-44AD-9A60-6973FEF39361',
'08929D40-863E-4D46-94BD-B4DF9352A855',
'C8BE80C4-AA0A-41ED-A44C-BCBE2CC980C0',
'8C3848C7-8621-43CF-A58D-D4A6ED4DC166',
'C244B01A-E9DD-4BF4-B336-1479A5A7C88D',
'632FAC1E-6B04-4A69-8BF2-0C2E2B0AD8AB'
) and
e.EpisodeDate between '2016-04-01' and '2016-12-15'
)
and p.PatientStatus = 'Current'
group by p.Identifier2, per.Forenames, per.Surname
So what this query is doing is finding certain people only if they haven't attended the specified episode types. Now what I want to do, is filter this even further. I want to make it so that if they have have attended the episode type with the ID '9254B31D-A304-498C-ADE4-F4003997C8FA' then they should still appear on this list, but only if their attended status is set to 'Yes'. Otherwise I don't want them to show up.
Where can I add this filter?
This type of logic is easier to do using group by and having:
select per.Forenames, per.Surname, p.Identifier2
from patient p join
Person per
on per.PersonID = p.PersonID join
Episode e
on e.PatientID = p.PatientID
group by per.Forenames, per.Surname, p.Identifier2
having sum(case when e.EpisodeTypeID in ('FCB9EAA0-C814-413E-A5FC-48547EF973B7',
'E422A8FA-839B-44AD-9A60-6973FEF39361',
'08929D40-863E-4D46-94BD-B4DF9352A855',
'C8BE80C4-AA0A-41ED-A44C-BCBE2CC980C0',
'8C3848C7-8621-43CF-A58D-D4A6ED4DC166',
'C244B01A-E9DD-4BF4-B336-1479A5A7C88D',
'632FAC1E-6B04-4A69-8BF2-0C2E2B0AD8AB'
) and
e.EpisodeDate between '2016-04-01' and '2016-12-15'
then 1 else 0
end) = 0 and
sum(case when e.EpisodeTypeId = '9254B31D-A304-498C-ADE4-F4003997C8FA' and
e.AttendedStatus <> 'Yes' -- assumes AttendedStatus is not NULL
then 1 else 0
end) = 0;
Each sum() expression counts the number of matches for the conditions
I would just add another condition for and exists if you want an additional condition above and beyond the existing conditions like this:
select per.Forenames, per.Surname, p.Identifier2
from patient p
...
and p.PatientStatus = 'Current'
and exists (
select 1
from Episode e1
where e1.PatientID = p.PatientID
and e1.EpisodeTypeID = '9254B31D-A304-498C-ADE4-F4003997C8FA'
and e1.AttendedStatus = 'Yes'
)
group by ...

joining no of reports in oracle

Hi I want to generate the report which will contain following things:
Daily report by country/destination against counts.
Eg.
No of contributions
No videos
No of experiences
Based on count yesterday, total for week, total for year for each destination.
I have written following query to count total for week.
select a.name, count(e.id),count(uc.id),count(v.id)
from accommodation a, user_contribution uc,experience_video ev, video v,
(select id "ID",user_contribution_id,accommodation_id,case
when modified_on is null then trunc(created_on)
else trunc(modified_on) end last_modified from experience)e
where
e.last_modified between trunc(sysdate)-7 and trunc(sysdate) and
e.accommodation_id = a.id and
e.user_contribution_id = uc.id and
e.id = ev.experience_id and
v.id = ev.video_id
group by a.name
union
select l.name, count(ex.id),count(uc.id),count(v.id),count(r.id)
from location l, user_contribution uc,experience_video ev, video v,
(select id "ID",user_contribution_id,location_id,case
when modified_on is null then trunc(created_on)
else trunc(modified_on) end last_modified from experience)ex
where
ex.last_modified between trunc(sysdate)-7 and
trunc(sysdate) and
l.id = ex.location_id and
ex.user_contribution_id = uc.id and
ex.id = ev.experience_id and
v.id = ev.video_id
group by l.name;
I can similarly write the query to extract weekly and yearly count.
basically I want all counts in one row for each destination.
Is there any better way of writing this query?
Would appreciate your help.

create from select(Query error)

create table1 as SELECT from c.Name, Value, Qtr, Value as SUM(Value)
from User.Pofile a,pet_dsa_qtr_table b,User.Count c,User.Service d
WHERE Category = 'PROFESSIONAL' and Item_Name = 'PROF_PKT_RECVD' and
convert(char(32),d.User_Id) = c.Name and Service_Name_Cd = 2
and Status = 2 and d.User_Id *= a.User_Id and c.Period = b.Period
and Module = 'ACCT_TRADES' and Type in ('EQ','OPTN')
GROUP BY Name, Item_Value,
Qtr HAVING SUM(Value) >= 10000 and Item_Value in ('0', NULL);
The above was a query that was created by me as an equivalent for sybase.Can anyone help me in correcting this query
You have some quite serious syntax inconsistencies in your query which I have tried to correct (with guesswork as you haven't provided a table structure). e.g. In the select list you select "Value" but in the GROUP BY you group on "Item_Value" etc.
You also need to fully qualify your columns, some you qualify, some (like "Module" and "Type" etc) you don't.
You might also want to add a storage clause too.
Here is my best educated guess at what you are trying to achieve:
CREATE TABLE table1
AS
SELECT c.Name,
Item_Value,
Qtr,
SUM(Value) as "SUM_Value"
FROM User.Pofile a
pet_dsa_qtr_table b,
User.Count c,
User.Service d
WHERE Category = 'PROFESSIONAL'
AND Item_Name = 'PROF_PKT_RECVD'
AND TO_CHAR(d.User_Id) = c.Name
AND Service_Name_Cd = 2
AND Status = 2
AND d.User_Id = a.User_Id(+)
AND c.Period = b.Period
AND Module = 'ACCT_TRADES'
AND Type in ('EQ','OPTN')
GROUP BY c.Name,
Item_Value,
Qtr
HAVING (SUM(Value) >= 10000
AND NVL(Item_Value, '0') = '0');

Problem with count in SQL query

I want to create a query that show the name of the employee and the sum of all service that aren't not closed. The result of the query is not giving me the correct value.
Here is my query:
SELECT DISTINCT
a.AssignedEmp,
COUNT( a.TipoStatus ) AS 'Service Request Count'
FROM Service a,
employee b
WHERE b.Classification_ClassificationID = 2
AND a.TipoStatus = 'Open'
OR a.TipoStatus = 'Pending'
OR a.TipoStatus = 'Hold'
OR a.TipoStatus = 'Warranty'
AND a.AssignedEmp = b.UserName
GROUP BY a.AssignedEmp
LIMIT 0, 30
There result that this query gives me is:
dpadil 16
epadil 8
The correct values are supposed to be 2 and 1.
The first thing you should do is get rid of the count and the group by.
This will give you the actual rows that are being used and will probably show you the problem straight up.
The fact that the ratios are correct (16:8 = 2:1) usually indicates that AND a.AssignedEmp = b.UserName is not as limiting as you think.
In other words, the cross-table joins are more prolific. Getting the rows themselves out should help you understand why.
You need brackets around your OR'd conditions (and should use JOIN notation):
SELECT DISTINCT
a.AssignedEmp,
COUNT( a.TipoStatus ) AS 'Service Request Count'
FROM Service AS a
JOIN employee AS b ON a.AssignedEmp = b.UserName
WHERE b.Classification_ClassificationID = 2
AND (a.TipoStatus = 'Open'
OR a.TipoStatus = 'Pending'
OR a.TipoStatus = 'Hold'
OR a.TipoStatus = 'Warranty')
GROUP BY a.AssignedEmp
LIMIT 0, 30
Or you can write the condition directly in terms of 'not closed':
SELECT DISTINCT
a.AssignedEmp,
COUNT( a.TipoStatus ) AS 'Service Request Count'
FROM Service AS a
JOIN employee AS b ON a.AssignedEmp = b.UserName
WHERE b.Classification_ClassificationID = 2
AND a.TipoStatus != 'Closed'
GROUP BY a.AssignedEmp
LIMIT 0, 30
As originally written, the WHERE clause was equivalent to:
WHERE (b.Classification_ClassificationID = 2 AND a.TipoStatus = 'Open')
OR a.TipoStatus = 'Pending'
OR a.TipoStatus = 'Hold'
OR (a.TipoStatus = 'Warranty' AND a.AssignedEmp = b.UserName)
This counts all the Open service items that have a classification ID of 2 for the user; it also counts all the Pending service items regardless of classification ID, and all the Hold service items regardless of classification ID, and all the Warranty service items assigned to the user, regardless of classification ID. The GROUP BY filters things so that only the items with the right assigned employee are counted, but there is a partial cross-product for some of the terms, leading to the inflated counts.
You probably want to structure your condition a bit more explicitly.
You should also specify the JOIN condition using a JOIN statement, rather than an additional condition.
SELECT DISTINCT
a.AssignedEmp,
COUNT( a.TipoStatus ) AS 'Service Request Count'
FROM Service a
INNER JOIN employee b
ON a.AssignedEmp = b.UserName
WHERE b.Classification_ClassificationID = 2
AND (
a.TipoStatus = 'Open'
OR a.TipoStatus = 'Pending'
OR a.TipoStatus = 'Hold'
OR a.TipoStatus = 'Warranty'
)
GROUP BY a.AssignedEmp
LIMIT 0, 30