Query build to find records where all of a series of records have a value - sql

Let me explain a little bit about what I am trying to do because I dont even know the vocab to use to ask. I have an Access 2016 database that records staff QA data. When a staff member misses a QA we assign a job aid that explains the process and they can optionally send back a worksheet showing they learned about what was missed. If they do all of these ina 3 month period they get a credit on their QA score. So I have a series of records all of whom have a date we assigned the work(RA1) and MAY have a work returned date(RC1).
In the below image "lavalleer" has earned the credit because both of her sheets got returned. "maduncn" Did not earn the credit because he didn't do one.
I want to create a query that returns to me only the people that are like "lavalleer". I tried hitting google and searched here and access.programmers.co.uk but I'm only coming up with instructions to use Not null statements. That wouldn't work for me because if I did a IS Not Null on "maduncn" I would get the 4 records but it would exclude the null.
What I need to do is build a query where I can see staff that have dates in ALL of their RC1 fields. If any of their RC1 fields are blank I dont want them to return.

Consider:
SELECT * FROM tablename WHERE NOT UserLogin IN (SELECT UserLogin FROM tablename WHERE RCI IS NULL);

You could use a not exists clause with a correlated subquery, e.g.
select t.* from YourTable t where not exists
(select 1 from YourTable u where t.userlogin = u.userlogin and u.rc1 is null)
Here, select 1 is used purely for optimisation - we don't care what the query returns, just that it has records (or doesn't have records).
Or, you could use a left join to exclude those users for which there is a null rc1 record, e.g.:
select t.* from YourTable t left join
(select u.userlogin from YourTable u where u.rc1 is null) v on t.userlogin = v.userlogin
where v.userlogin is null
In all of the above, change all occurrences of YourTable to the name of your table.

Related

SQL: updating a column in one table based on a count result from another table

I've got a table called GUEST and a table called HOTEL_BOOKING.
I want to update a column in the GUEST table called guest_nobookings (this is the number of bookings a guest has made at the hotel).
I can get the number of bookings a guest has made from the HOTEL_BOOKING table by performing a count on the hotel booking number based on the guest number.
This is what I have so far:
SELECT COUNT(hotel_bookingno)
FROM hotel_booking
GROUP BY guest_no;
That gives me the number of bookings for each guest who has stayed at the hotel. To update the GUEST table I've tried this:
UPDATE guest
SET (guest_nobookings = (SELECT COUNT(hotel_bookingno)
FROM hotel_booking
GROUP BY guest_no));
I get a 'single-row subquery returns more than one row' error when I try this however.
Is there a more direct way of solving this problem?
Thanks in advance!
You could use a correlated subquery to update here:
UPDATE guest g
SET guest_nobookings = (SELECT COUNT(*) FROM hotel_booking hb
WHERE hb.guest_no = g.hotel_bookingno);
You may need to alter the above slightly to make it work. As a side note, you might want to rethink your design and avoid even doing this update. The reason is that each time the state in the hotel_booking table changes, the count aggregates might become invalid, and would have to recomputed. In general, we try to avoid storing aggregates of original data in SQL.
Why you want to have sane data in two tables? It will lead to inconsitent data in one of them. (As already described by Tim)
My suggestion is to use the view for all details of the guest and in the view use count from hotel_bookings table.
If you really want to update the count then You can use merge statement as following:
Merge into guest g
Using (select COUNT(*) as cnt, hb.guest_no
FROM hotel_booking hb
GROUP BY hb.guest_no) hb
On (hb.guest_no = g.hotel_bookingno)
When matched then
Update set g.guest_nobookings = hb.cnt;
Cheers!!
UPDATE guest
SET guest_nobookings = (
WITH t1 AS (
SELECT COUNT(*) no_bk, guest_no FROM hotel_booking
GROUP BY hotel_booking.guest_no
)
SELECT no_bk FROM t1
WHERE guest_no = guest.hotel_bookingno
);
This is another way to update the table using WITH clause.

Multiply total number of values in column by value in a different table

I am trying to count all the values in one column and then multiply this number by a value in a different table. So far I have:
SELECT CLUB_FEE * COUNT(MEMBER_ID) AS VALUE
FROM CLUB, SUBSCRIPTION
WHERE CLUB_ID = 'CLUB1';
This is not working however, can anyone please help?
I also need help doing this for multiple clubs. Is it possible to do it all in one statement for all clubs and then get the average?
Presumably, you intend something like this:
SELECT MAX(c.CLUB_FEE) * COUNT(MEMBER_ID) AS VALUE
FROM CLUB c JOIN
SUBSCRIPTION s
ON c.CLUB_ID = s.CLUB_ID
WHERE c.CLUB_ID = 'CLUB1';
You can also write this as:
SELECT SUM(c.CLUB_FEE) AS VALUE
FROM CLUB c JOIN
SUBSCRIPTION s
ON c.CLUB_ID = s.CLUB_ID
WHERE c.CLUB_ID = 'CLUB1';
I thought the first version would be clearer, because the OP specifies COUNT() in the question.
If you want it for all clubs that have subscribers:
SELECT SUM(c.CLUB_FEE) AS VALUE
FROM CLUB c JOIN
SUBSCRIPTION s
ON c.CLUB_ID = s.CLUB_ID
GROUP BY c.CLUB_ID;
From inspecting the explain plans, it seems the following version may be a bit more efficient (since it avoids a join and uses only one aggregation). If you need this for ALL clubs at the same time, then probably all solutions will have the same "optimizer cost" (they will all do a join at some point).
select club_fee * (select count(member_id) from subscription where club_id = 'CLUB1')
from club
where club_id = 'CLUB1'
So now the only aggregate function is pushed into a subquery and the rest does not need either a join or another aggregate function.
Of course, this only matters if performance is important; it may very well not be.

SQL Server : join on array of ID's from previous join

I have 2 tables. One has been pruned to show only ID's which meet certain criteria. The second needs to be pruned to show only data that matches the previous "array" of id's. there can be multiple results.
Consider the following:
Query_1_final: Returns the ID's of users whom meet certain criteria:
select
t1.[user_id]
from
[SQLDB].[db].[meeting_parties] as t1
inner join
(select distinct
[user_id]
from
[SQLDB].[db].[meeting_parties]
group by
[user_id]
having
count([user_id]) = 1) as t2 on t1.user_id = t2.user_id
where
[type] = 'organiser'
This works great and returns:
user_id
--------------------
22
1255
9821
and so on...
It produces a single column with the ID's of everyone who is a "Meeting Organizer" and also in the active_meetings table. (note, there are multiple types/roles, this was the best way to grab them all)
Now, I need this data to filter another table, another join. Here is the start of my query
Query_2_PREP: returns 5 columns where the meeting has "started" already.
SELECT
[meeting_id]
,[meeting_style]
,[meeting_day]
,[address]
,[promos]
FROM
[SQLDB].[db].[all_meetings]
WHERE
[meeting_started] = 'TRUE'
This works as well
meeting_id | meeting_style | meeting_day ...
---------------------------------------------
23 open M,F,SA
23 discussion TU,TH
23 lead W,F
and so on...
and returns ALL 10,982 meetings that started, but I need it to return only the meetings that are from the distinct 'organiser's ID's from Query_1_final (which should be more like 1200 records or so)
Ideally, I need something "like" this below (but of course it does not work)
Query 2: needs to return all meetings that are from organiser ID's only.
SELECT
[meeting_party_id]
,[meeting_style]
,[meeting_day]
,[address]
,[promos]
FROM
[SQLDB].[db].[all_meetings]
WHERE
[meeting_started] = 'TRUE'
AND [meeting_party_id] = "ANY Query_1_final results, especially multiple"
I have tried nesting JOIN and INNER JOIN's but I think there is something fundamental I am missing here about SQL. In PHP I would use an array compare or just run another query... any help would be much appreciated.
Just use IN. Here is the structure of the logic:
with q1 as (
<first query here>
)
SELECT m.*
FROM [SQLDB].[db].[all_meetings] m
WHERE meeting_started = 'TRUE' AND
meeting_party_id IN (SELECT user_id FROM q1);

SQL - Where Field has Changed Over Time

I'm running SQL server management studio and my table/dataset contains approximately 700K rows. The table is a list of accounts each month. So at the begining of each month, a snapshot is taken of all the accounts (and who owns them), etc. etc. etc. and that is used to update the data-set. The 2 fields in question are AccountID and Rep (and I guess you could say month). This query really should be pretty easy but TBH, I have to move-on to other things so I thought I'd throw it up here to get some help.
Essentially, I need to extract distinct AccountIDs that at some point changed reps. See a screenshot below of what I'm thinking:
Thoughts?
--- I should note for instance that AccountID ABC1159 is not included in the results b/c it appears only once and is never handled by any other rep.
--- Also, another parameter is if the first time an account appears and the rep name appears in a certain list and then moves to another rep, that's fine. For instance, if the first instance of a Rep was say "Friendly Account Manager" or "Support Specialist" and then moves to another name, those can be excluded from the result field. So we essentially need a where statement or something that eliminates those results if the first instance appears in this list, then there is an instance where the name changed but non after that. The goal is to see if after the rep received a human rep (so they didn't have a name in that list), did they then change human reps at a certain point in time.
Try this:
SELECT t.AccountID
FROM [table] t
WHERE NOT EXISTS(SELECT * FROM [reps table] r WHERE r.Rep = t.Rep AND r.[is not human])
GROUP BY t.AccountID
HAVING COUNT(DISTINCT t.Rep) > 1;
You want to first isolate the distinct AccountID and Rep combinations, then you want to use GROUP BY and HAVING to find AccountID values that have multiple Rep values:
SELECT AccountID
FROM (SELECT DISTINCT AccountID, Rep
FROM YourTable
WHERE Rep NOT IN ('Support Specialist','Friendly Account Manager')
)sub
GROUP BY AccountID
HAVING COUNT(*) > 1
Try a SELECT DISTINCT on the table and join the table to itself - something like this - with Account being the name I gave your table:
SELECT DISTINCT A1.AccountID, A1.Rep, A1.ReportMonth
FROM AccountTable.AccountID A1
INNER JOIN AccountTable A2
ON A1.AccountID = A2.AccountID
AND A1.Rep <> A2.Rep
ORDER BY A1.AccountD

How can I compare two tables and delete on matching fields (not matching records)

Scenario: A sampling survey needs to be performed on membership of 20,000 individuals. Survey sample size is 3500 of the total 20000 members. All membership individuals are in table tblMember. Same survey was performed the previous year and members whom were surveyed are in tblSurvey08. Membership data can change over the year (e.g. new email address, etc.) but the MemberID data stays the same.
How do I remove the MemberID/records contained tblSurvey08 from tblMember to create a new table of potential members to be surveyed (lets call it tblPotentialSurvey09). Again the record for a individual member may not match from the different tables but the MemberID field will remain constant.
I am fairly new at this stuff but I seem to be having a problem Googling a solution - I could use the EXCEPT function but the records for the individuals members are not necessarily the same from one table to next - just the MemberID may be the same.
Thanks
SELECT
* (replace with column list)
FROM
member m
LEFT JOIN
tblSurvey08 s08
ON m.member_id = s08.member_id
WHERE
s08.member_id IS NULL
will give you only members not in the 08 survey. This join is more efficient than a NOT IN construct.
A new table is not such a great idea, since you are duplicating data. A view with the above query would be a better choice.
I apologize in advance if I didn't understand your question but I think this is what you're asking for. You can use the insert into statement.
insert into tblPotentialSurvey09
select your_criteria from tblMember where tblMember.MemberId not in (
select MemberId from tblSurvey08
)
First of all, I wouldn't create a new table just for selecting potential members. Instead, I would create a new true/false (1/0) field telling if they are eligible.
However, if you'd still want to copy data to the new table, here's how you can do it:
INSERT INTO tblSurvey00 (MemberID)
SELECT MemberID
FROM tblMember m
WHERE NOT EXISTS (SELECT 1 FROM tblSurvey09 s WHERE s.MemberID = m.MemberID)
If you just want to create a new field as I suggested, a similar query would do the job.
An outer join should do:
select m_09.MemberID
from tblMembers m_09 left outer join
tblSurvey08 m_08 on m_09.MemberID = m_08.MemberID
where
m_08.MemberID is null