SQL Count based on value in another table - sql

I need to do two counts of a column based on the content of another column in a different table.
SELECT WMTRANSACTIONHEADER.TRANSACTIONTYPE
, IMITEM.ITEMSUPCCODE
, COUNT(IMITEM.ITEMSUPCCODE) AS 'Shipment'
, COUNT(IMITEM.ITEMSUPCCODE) AS 'Reciept'
, CAST(MTRANSACTIONHEADER.TRANSACTIONDATE AS DATE) AS DATE
FROM WMTRANSACTIONHEADER
INNER JOIN WMTRANSACTIONDETAIL ON WMTRANSACTIONHEADER.ROWID = WMTRANSACTIONDETAIL.R_TRANSACTIONHEADER
INNER JOIN IMITEM ON WMTRANSACTIONDETAIL.R_ITEM = IMITEM.ROWID
Forgive me for how unreadable it is, but generally, I'm trying to count ITEMSUPCCODE based on whether TRANSACTIONTYPE is either an 'R' or an 'S'. Any tips/ideas would be great!
EDIT:
Unfortunately I underestimated this task and the importance of other fields. So let me explain this more in depth. I am making a report that counts the daily ITEMSUPCCODE fields. There are 8 ITEMSUPCCODE fields that are possible, and we want a count of each one. So the perfect report would group all the transactions in a day, then group by each entry in ITEMSUPCCODE, and then count each entry in ITEMSUPCCODE, based on what is in TRANSACTIONTYPE. I apologize for not being clear, I thought the counting part was the only issue I had!

Something like this if I am getting your question right: Below query is not tested
With tmp AS(Select WMTRANSACTIONHEADER.TRANSACTIONTYPE, IMITEM.ITEMSUPCCODE,
CONVERT(DATETIME, CONVERT(DATE, MTRANSACTIONHEADER.TRANSACTIONDATE)) Date;
FROM WMTRANSACTIONHEADER INNER JOIN WMTRANSACTIONDETAIL
ON WMTRANSACTIONHEADER.ROWID = WMTRANSACTIONDETAIL.R_TRANSACTIONHEADER
INNER JOIN IMITEM ON WMTRANSACTIONDETAIL.R_ITEM = IMITEM.ROWID
WHERE TRANSACTIONTYPE in ('R', 'S'))
select count(ITEMSUPCCODE) , Date, Transactiontype
from tmp
group by Date, Transactiontype
You can change count(ITEMSUPCCODE) or count(DISTINCT ITEMSUPCCODE) based on your requirement.

Here's a different approach with the result in rows instead of columns.
It also allows you to count how many distinct IMITEM.ITEMSUPCCODE there are in your table
Select count(distinct IMITEM.ITEMSUPCCODE), --count of distinct values in the group
count(IMITEM.ITEMSUPCCODE), --non-distinct values
TRANSACTIONTYPE
FROM WMTRANSACTIONHEADER
INNER JOIN WMTRANSACTIONDETAIL
ON WMTRANSACTIONHEADER.ROWID = WMTRANSACTIONDETAIL.R_TRANSACTIONHEADER
INNER JOIN IMITEM
ON WMTRANSACTIONDETAIL.R_ITEM = IMITEM.ROWID
where TRANSACTIONTYPE in ('R', 'S')
group by TRANSACTIONTYPE

In cases like this, I often use a subquery like this:
select
NameLast,
(select count(*) from Paychecks where Paychecks.EmployeeID = Employees.ID) As PaycheckCount
from Employees
The inner join isn't needed for the subtable.
Reworking your original query, I think this should work:
SELECT WMTRANSACTIONHEADER.TRANSACTIONTYPE, IMITEM.ITEMSUPCCODE, COUNT(IMITEM.ITEMSUPCCODE) AS 'Shipment',
(SELECT COUNT(ITEMSUPCCODE) FROM IMITEM WHERE WMTRANSACTIONDETAIL.R_ITEM = IMITEM.ROWID) AS 'Reciept'
FROM WMTRANSACTIONHEADER INNER JOIN WMTRANSACTIONDETAIL ON
WMTRANSACTIONHEADER.ROWID = WMTRANSACTIONDETAIL.R_TRANSACTIONHEADER
Does this work for you?

Related

SQL joined tables are causing duplicates

So table A is an overall table of policy_id information, while table b is policy_id's with claims attached. Not all of the id's in A exist in B, but I want to join the two tables and sum(total claims).
The issue is that the sum is way higher than the actual sum within the table itself.
Here is what I've tried so far:
select a.policy_id, coalesce(sum(b.claim_amt), 0)
from database.table1 as a
left join database2.table2 as b on a.policy_id = b.policy_id
where product_code = 'CI'
group by a.policy_id
The id's that don't exist in b show up just fine with a 0 next to them, it's the ones that do exist where the claim_amt's seem like they're being duplicated heavily in the sum.
I suspect your policy_id in table1 are not unique and that leads to the doubled,tripled ,etc. amounts
You could aggregate the sums from table2 in a CTE to get around this.
WITH CTE AS (
SELECT
policy_id
coalesce(sum(claim_amt), 0) as sum_amt
FROM database2.table2
group by policy_id
)
select a.policy_id, b.sum_amt
from database.table1 as a
left join CTE as b on a.policy_id = b.policy_id
where product_code = 'CI'

Inner join in my query seems to not be recognized

I have a little question about a problem with a SQL query. I've written a query to get some records and the number of occurences of those records. The problem is that I'd like to join two tables so I can get some values instead of foreign keys. This is my actual query (which I think is wrong because there is no joins in the result) :
SELECT date, heure_debut, heure_fin, Event_id,
horaire_id, local_id, enseignant_id, COUNT(*) doublons
FROM `reservations`
INNER JOIN `events` ON `events`.`id` = `reservations`.`Event_id`
INNER JOIN `couleurs` ON `couleurs`.`id` = `events`.`couleur_id`
GROUP BY date,
heure_debut,
heure_fin,
Event_id,
horaire_id,
local_id,
enseignant_id
HAVING COUNT(*) > 1 OR COUNT(*) = 1
Thank you for your answers.
Sort of a wild guess based on the OP's description of his intent.
Assuming that table events has a column titre that is an AK, the following modifications to the original query might mirror this intent. Instead of the event id it shows its name:
SELECT date, heure_debut, heure_fin, `events`.titre,
horaire_id, local_id, enseignant_id, COUNT(*) doublons
FROM `reservations`
INNER JOIN `events` ON `events`.`id` = `reservations`.`Event_id`
INNER JOIN `couleurs` ON `couleurs`.`id` = `events`.`couleur_id`
GROUP BY date,
heure_debut,
heure_fin,
`events`.titre,
horaire_id,
local_id,
enseignant_id
HAVING COUNT(*) > 0
;

Oracle sql query that returns customers who are coming for the first time in property

I have 3 tables: customer, property and stays. Table customer contains all the data about customers (customer_id, name, surname, email...). Table property contains the list of all the properties (property_id, property_name...) and table stays contains all the earlier stays of customers (customer_id, property_id, stay_id, arrival_date, departure_date...).
Some customers stayed multiple times in more than one properties and some customers are coming for the first time.
Can someone please explain oracle sql query which returns only the customers who are stying in any of the properties for the first time.
Sorry guys for answering late..
This is what I got so far.
Tables:
Customer $A$
Stays $B$
Property $C$
Customer_Fragments $D$
SELECT a.RIID_,
sub.CUSTOMER_ID_,
sub.PROPERTY_ID,
b.ARRIVAL_DATE,
c.PROPERTY_SEGMENT,
ROW_NUMBER() OVER (
PARTITION BY sub.CUSTOMER_ID_,
sub.PROPERTY_ID
ORDER BY
sub.CUSTOMER_ID_ asc
) RN
FROM
(
SELECT
b.CUSTOMER_ID_,
b.PROPERTY_ID
FROM
$B$ b
GROUP BY
b.CUSTOMER_ID_,
b.PROPERTY_ID
HAVING
COUNT(*)= 1
) sub
INNER JOIN $A$ a ON sub.CUSTOMER_ID_ = a.CUSTOMER_ID_
INNER JOIN $B$ b ON sub.CUSTOMER_ID_ = b.CUSTOMER_ID_
INNER JOIN $C$ c ON sub.PROPERTY_ID = c.PROPERTY_ID
LEFT JOIN $D$ d ON a.RIID_ = d.RIID_
WHERE
b.ARRIVAL_DATE = TRUNC(SYSDATE + 7)
AND c.PROPERTY_DESTINATION = 'Destination1'
AND lower(c.NAME_) NOT LIKE ('unknown%')
AND a.NWL_PERMISSION_STATUS = 'I'
AND a.EMAIL_DOMAIN_ NOT IN ('abuse.com', 'guest.booking.com')
AND (d.BLACKLISTED != 'Y'
or d.BLACKLISTED is null
)
I want to select all customers who will come to Destination1, 7days from today to inform them about some activities. Customers can book several
properties in Destination1 and have the same arrival date (example: I can book a room in property1 for me and my wife and also book a room in property2 for my friends.. and we all come to destination1 on the same arrival date).
When this is the case I want to send just one info email to a customer and not two emails. The above SQL query returns two rows when this is the case and I want it to return just one row (one row = one email).
It is always required to post a code you have already tried to write so that we can than help you with eventual mistakes.
After that being said, I'll try to help you nevertheless, writing the full code.
Try this:
select cus.*
from customers cus
join stays st
on st.customer_id = cus.customer_id
where st.arrival_date >= YOUR_DATE --for example SYSDATE or TRUNC(SYSDATE)
and 1 = (select count(*)
from stays st2
where st2.customer_id = cus.customer_id)
You haven't specified it, but I GUESS that you are interested in getting the first-time-customers whose arrival date will be at or after some specified date. I've written that into my code in WHERE clause, where you should input such a date.
If you remove that part of the WHERE clause, you'll get all the customers that stayed just once (even if that one and only stay was 10 years ago, for example). What's more, if you remove that part of the code, you can than also remove the join on stays st table from the query too, as the only reason for that join was the need to have access to arrival date.
If you need explanations for some other parts of the code too, ask in the comments for this answer.
I hope I helped!
WITH first_stays (customer_id, property_id, first_time_arrival_date, stay_no) AS
(
SELECT s.customer_id
, s.property_id
, s.arrival_date AS first_time_arrival_date
, ROW_NUMBER() OVER (PARTITION BY s.customer_id, s.property_id ORDER BY s.arrival_date) stay_no
FROM stays s
)
SELECT c.surname AS customer_surname
, c.name AS customer_name
, p.property_name
, s.first_time_arrival_date
FROM customer c
INNER
JOIN first_stays s
ON s.customer_id = c.customer_id
AND s.stay_no = 1
INNER
JOIN property p
ON p.property_id = s.property_id
The first part WITH first_stays is a CTE (Common Table Expression, called subquery factoring in Oracle) that will number the stays for each pair (customer_id, property_id) ordered by the arrival date using a window function ROW_NUMBER(). Then just join those values to the customer and property tables to get their names or whatever, and apply stay_no = 1 (first stay) condition.
If I understand the question correctly, this is not a complicated query:
select c.*
from c join
(select s.customer_id
from stays s
group by s.customer_id
having count(*) = 1
) s
on s.customer_id = c.customer_id;

Joining two most recent events from two tables

I'm trying to build a report in SQL that shows when a patient last received a particular lab service and the facility at which they've received that service. Unfortunately, the lab procedure and facility are in different tables. Here is what I have now (apologies in advance for my weird aliasing, it makes better since with the actual table names):
;with temp as (Select distinct flow.pid, flow.labdate as obsdate, flow.labvalue as obsvalue
From labstable as flow
Where flow.name = 'lab name'
)
Select distinct p.patientid, MAX(temp.obsdate) [Last Reading], COUNT(temp.obsdate) [Number of Readings],
Case
When count(temp.obsdate) > 2 then 'Active'Else 'Inactive' End [Status], facility.NAME [Facility]
From Patientrecord as p
Join temp on temp.pid = p.PId
Join (Select loc.name, MAX(a.apptstart)[Last appt], a.patientid
From Appointmentstable as a
Join Facility as loc on loc.facilityid = a.FacilityId
Where a.ApptStart = (Select MAX(appointments.apptstart) from Appointments where appointments.patinetId = a.patientid)
Group by loc.NAME, a.patientId
) facility on facility.patientId = p.PatientId
Group by p.PatientId, facility.NAME
Having MAX(temp.obsdate) between DATEADD(yyyy, -1, GETDATE()) and GETDATE()
Order by [Last Reading] asc
My problem with this is that if the patient has been to more than one facility within the time frame, the subquery is selecting each facility into the join, inflating the results by apprx 4000. I need to find a way to select ONLY the VERY MOST RECENT facility from the appointments list, then join it back to the lab. Labs do not have a visitID (that would make too much sense). I'm fairly confident that I'm missing something in either my subquery select or the corresponding join, but after four days I think I need professional help.
Suggestions are much appreciated and please let me know where I can clarify. Thank you in advance!
Change your subquery with alias "facility" to something like this:
...
join (
select patientid, loc_name, last_appt
from (
select patientid, loc_name=loc.name, last_appt=apptstart,
seqnum=row_number() over (partition by patientid order by apptstart desc)
from AppointmentsTable a
inner join Facility loc on loc.facilityid = a.facilityid
) x
where seqnum = 1
) facility
on ...
...
The key difference is the use of the row_number() windowing function. The "partition by" and "order by" clauses guarantee you'll get one set of row numbers per patient and the row with the most recent date will be assigned row number 1. The filter of "seqnum = 1" makes sure you get only the one row you want for each patient.

Select SUM from multiple tables

I keep getting the wrong sum value when I join 3 tables.
Here is a pic of the ERD of the table:
(Original here: http://dl.dropbox.com/u/18794525/AUG%207%20DUMP%20STAN.png )
Here is the query:
select SUM(gpCutBody.actualQty) as cutQty , SUM(gpSewBody.quantity) as sewQty
from jobOrder
inner join gpCutHead on gpCutHead.joNum = jobOrder.joNum
inner join gpSewHead on gpSewHead.joNum = jobOrder.joNum
inner join gpCutBody on gpCutBody.gpCutID = gpCutHead.gpCutID
inner join gpSewBody on gpSewBody.gpSewID = gpSewHead.gpSewID
If you are only interested in the quantities of cuts and sews for all orders, the simplest way to do it would be like this:
select (select SUM(gpCutBody.actualQty) from gpCutBody) as cutQty,
(select SUM(gpSewBody.quantity) from gpSewBody) as sewQty
(This assumes that cuts and sews will always have associated job orders.)
If you want to see a breakdown of cuts and sews by job order, something like this might be preferable:
select joNum, SUM(actualQty) as cutQty, SUM(quantity) as sewQty
from (select joNum, actualQty, 0 as quantity
from gpCutBody
union all
select joNum, 0 as actualQty, quantity
from gpSewBody) sc
group by joNum
Mark's approach is a good one. I want to suggest the alternative of doing the group by's before the union, simply because this can be a more general approach for summing along multiple dimensions.
Your problem is that you have two dimensions that you want to sum along, and you are getting a cross product of the values in the join.
select joNum, act.quantity as ActualQty, q.quantity as Quantity
from (select joNum, sum(actualQty) as quantity
from gpCutBody
group by joNum
) act full outer join
(select joNum, sum(quantity) as quantity
from gpSewBody
group by joNum
) q
on act.joNum = q.joNum
(I have kept Mark's assumption that doing this by joNum is the desired output.)