Multiple records in subquery error - sql

I am using MS Access 2010.
I am trying to create a table with three columns by joining different sections of the same table
the dataset is
ID Date Job Title Count of ID
-- --------- ------------------------------------------------------ -----------
1 13-May-14 Security Guards 15
2 13-May-14 Interpreters and Translators 22
3 13-May-14 Laborers and Freight, Stock, and Material Movers, Hand 1
4 13-May-14 Maintenance and Repair Workers, General 23
5 13-May-14 Protective Service Workers, All Other 54
6 13-May-14 Logisticians 65
7 02-Apr-14 Security Guards 88
8 02-Apr-14 Interpreters and Translators 22
9 02-Apr-14 Laborers and Freight, Stock, and Material Movers, Hand 44
10 02-Apr-14 Maintenance and Repair Workers, General 77
11 02-Apr-14 Protective Service Workers, All Other 66
12 02-Apr-14 Logisticians 8
and the query is
SELECT ctrjob.[Job Title], ctrjob.[Count of ID],
(Select ctrjob.[Count of ID] from ctrjob
where ctrjob.[Job Title] = ctrjob.[Job Title] and ctrjob.[Date] = #4/2/2014#)
FROM ctrjob where [ctrjob].[Date] = #5/13/2014# ;
I want my finished table to create a table that places the number of contractors in each job title on 5/13/2014 next to the number from 4/2/2014. The goal is to do a comparison of the change in the number of workers.
But I keep getting an error message that
At most one record can be returned by the subquery
But all I want is for it to return the one corresponding record from the other date. Each time it is executed it should only return the number in job category X on 4/2/2014.
I don't want to use a transform/crosstab because the full dataset has too many dates and I want to use a form to toggle dates in the end. The list of job titles is identical across all dates. I am using MS Access 2010.

Instead of a subquery, use a join:
SELECT a.[Job Title], a.[Count of ID], b.[Count of ID]
FROM ctrjob a INNER JOIN
ctrjob b ON a.[Job Title] = b.[Job Title]
WHERE a.Date = #4/2/2014# AND
b.Date = #5/13/2014#

Related

Using TOP 1 (or CROSS APPLY) within multiple joins

I've reviewed multiple Q&A involving TOP 1 and CROSS APPLY (including the very informative 2043259), but I still can't figure out how to solve my issue. If I had a single join I'd be fine, but fitting TOP 1 into the middle of a chain of joins has stumped me.
I have four tables and one of the tables contains multiple matches when joining due to a previous bug (since fixed) that created new records in the table instead of updating existing records. In all cases, where there are multiple records, it is the top-most record that I want to use in one of my joins. I don't have access to the table to clean up the extraneous data, so I just have to deal with it.
The purpose of my query is to return a list of all "Buildings" managed by a particular person (user choses a person's name and they get back a list of all buildings managed by that person). My tables are:
Building (a list of all buildings):
BuildingId BuildingName
1 Oak Tree Lane
2 Lighthoue Court
3 Fairview Lane
4 Starview Heights
WebBuildingMapping (mapping of BuidingId from Building table, that is part of an old system, and corresponding WebBuildingId in another piece of software):
BuildingId WebBuildingId
1 201
2 202
3 203
4 204
WebBuildingContacts (list of ContactID for the building manager of each building). This is the table with duplicate values - where I want to choose the TOP 1. In sample data below, there are two references to WebBuidingId = 203 (row 3 & row 5) - I only want to use row 3 data in my join.
Id WebBuildingId ContactId
1 201 1301
2 202 1301
3 203 1303
4 204 1302
5 203 1302
Contacts (list of ContactIds and corresponding property manager Names)
ContactId FullName
1301 John
1302 Mike
1303 Judy
As noted, in the example above, the table WebBuildingContact has two entries for the building with a WebBuidingId = 203 (row 3 and row 5). In my query, I want to select the top one (row 3).
My original query for a list of buildings managed by 'Mike' is:
SELECT BuildingName
FROM Building bu
JOIN WebBuildingMapping wbm ON wbm.BuildingId = bu.BuildingId
JOIN WebBuildingContact wbc ON wbc.WebBuildingId = wbm.WebBuildingId
JOIN Contacts co ON co.ContactId = wbc.ContactId
WHERE co.FullName = 'Mike'
This returns 'Fairview Lane' and 'Starview Heights'; however, Judy manages 'Fairview Lane' (she's the top entry in the WebBuildingContacts table). To modify the query and eliminate row 5 in WebBuildingContacts from the join, I did the following:
SELECT BuildingName
FROM Building bu
JOIN WebBuildingMapping wbm ON wbm.BuildingId = bu.BuildingId
JOIN WebBuildingContact wbc ON wbc.WebBuildingId =
(
SELECT TOP 1 WebBuildingId
FROM WebBuildingContact
WHERE WebBuildingContact.WebBuildingId = wbm.WebBuildingId
)
JOIN Contacts co ON co.ContactId = wbc.ContactId
WHERE co.FullName = 'Mike'
When I try this; however, I get the same result set (ie it returns 'Mike' as manager for 2 buildings). I've also made various attempts to use CROSS APPLY but I just end up with 'The multi-part identifier could not be bound', which is a whole other rabbit hole to go down.
You could try this:
SELECT bu2.BuildingName
FROM building bu2
WHERE bu2.BuildingId IN
(SELECT MAX(bu.BuildingId)
FROM Building bu
JOIN WebBuildingMapping wbm ON wbm.BuildingId = bu.BuildingId
JOIN WebBuildingContact wbc ON wbc.WebBuildingId = wbm.WebBuildingId
JOIN Contacts co ON co.ContactId = wbc.ContactId
WHERE co.FullName = 'Mike'
);

Duplicate rows because 1 column has multiple distinct values

I'm running a SELECT query to get data across multiple tables in the same server instance. However I've just noticed that the rows pulled on some data get duplicated because the main table I'm pulling from has a few different values in one of the columns. Here's the query:
SELECT DISTINCT BIF030.C_ACCOUNT AS ACCOUNTNUMBER,
BIF003.C_ACCOUNTTYPE AS ACCOUNTTYPECODE,
CON013.C_DESCRIPTION AS ACCOUNTTYPE,
BIF003.C_DIVISION AS ZONE_DIVISONCODE,
CON028.C_DESCRIPTION AS ZONE_DIVISION,
BIF030.C_METER as METERNUMBER,
BIF005.C_METERCUSTOM1 AS REGISTERNUMBER,
CONVERT(DECIMAL(20,2), BIF030.N_CONSUMP) AS CONSUMPTION,
CON007.C_DESCRIPTION AS UNITS,
BIF030.T_READDATE AS READINGDATE,
MONTH(BIF030.T_READDATE) AS READINGMONTH,
DAY(BIF030.T_READDATE) AS READINGDAY,
YEAR(BIF030.T_READDATE) AS READINGYEAR,
BIF030.I_DAYS AS READINGDAYSCOUNT
FROM ADVANCED.BIF030
LEFT JOIN ADVANCED.CON007 ON CON007.C_UNITS=BIF030.C_UNITS
LEFT JOIN ADVANCED.BIF005 ON BIF005.C_METER=BIF030.C_METER
LEFT JOIN ADVANCED.BIF003 ON BIF003.C_ACCOUNT=BIF030.C_ACCOUNT
LEFT JOIN ADVANCED.CON013 ON CON013.C_ACCOUNTTYPE=BIF003.C_ACCOUNTTYPE
LEFT JOIN ADVANCED.CON028 ON CON028.C_DIVISION=BIF003.C_DIVISION
WHERE T_READDATE > '01-01-2014'
ORDER BY ACCOUNTNUMBER, READINGDATE ASC
I know SELECT DISTINCT is frowned upon, but I get even more rows without it. Here's a sample of what the data looks like when pulled:
ACCOUNTNUMBER
ACCOUNTTYPECODE
ACCOUNTTYPE
ZONE_DIVISIONCODE
ZONE_DIVISION
METERNUMBER
REGISTERNUMBER
CONSUMPTION
UNITS
READINGDATE
READINGMONTH
READINGDAY
READINGYEAR
READINGDAYSCOUNT
1234567
SP
ACCOUNT TYPE 1
00
00-NO ZONE
123456789
987654321
3.00
Thousands of Gallons
2014-01-16 00:00:00.00
1
16
2014
30
1234567
MF
ACCOUNT TYPE 2
02
02-GRAVITY
123456789
987654321
3.00
Thousands of Gallons
2014-01-16 00:00:00.00
1
16
2014
30
1234567
SR
ACCOUNT TYPE 3
02
02-GRAVITY
123456789
987654321
3.00
Thousands of Gallons
2014-01-16 00:00:00.00
1
16
2014
30
I also know the column that is messing this up is the "AccountTypeCode" because other accounts that don't have multiple codes associated with the "AccountNumber" only show 1 set of rows. So this one specifically (and probably others) is tripling the amount of rows pulled when it should only pull one for each "ReadingDate".
Also if anyone knows a good way to optimize the query I'd be happy to learn. I know just enough SQL to be dangerous, but not enough to figure this out. Thanks.
Ok. So good news and I want to add this in case it helps anyone else in the future. I found out that since the ACCOUNTTYPECODE and ZONE_DIVISIONCODE were coming from the table BIF003 I needed to add more in the WHERE statement. This is what fixed it for me:
AND BIF030.C_CUSTOMER = BIF003.C_CUSTOMER
Because the C_CUSTOMER column was different (it's a column in the BIF003 and BIF030 tables) which lead to the separate ACCOUNTTYPECODE results I need to check it in the WHERE statement.
Thanks everyone for kick starting my brain on this one.

Compare a date with a block of dates in SQL

i tried to ready a lot of date comparisons that i found here on stackoverflow and spread into the internet but i wasn't able to find the solution.
I have the following table (Trips):
VehicleID DriverID xID CheckIn CheckOut DateHour
462 257 7 1 0 16/12/2017 20:40:00
462 257 7 0 1 19/12/2017 10:05:00
5032 3746 11 1 0 02/10/2017 07:00:00
5032 3746 11 0 1 06/10/2017 17:00:00
When my company receives a traffic ticket, i want to compare the date from the ticket with the hole block of dates from the table "Trips", each block starts with CheckIn = 1 and finishes with CheckOut = 1, so this way i will know which driver was responsable for the ticket through the DriverID.
For example: the traffic ticket date and time are: 17/12/2017 08:00:00 and the Vehicle is the one with id = 462, i'll insert this date and time in a field in our system to consult automaticaly which driver was driving that car at that moment, we won´t use the ticket table yet. Looking at my example, i know it should return DriverID = 257, but theres a lot of trips with the same vehicle and diferent drivers.....The major problem is how can i compare the Date and Hour from the Ticket with the range of dates from the trips, since i have to consider 1 trip = 2 lines in the table
Unfortunately i can't change the way this table was created, cause we need this 2 lines, CheckIn and CheckOut, separately.
Any thoughts or directions?
Thank you for your attention
select t1.VehicleID
,t1.DriverID
,t1.xID
,t1.DateHour as Checkin
,t2.DateHour as Checkout
from trips as t1 join trips as t2 --self join trips to get both start and end in a single row
on t1.VehicleID = t2.VehicleID -- add all columns
and t1.DriverID = t2.DriverID -- which define
and t1.xID = t2.xID -- a unique trip
and t1.Checkin = 1 -- start
and t2.Checkout = 1 -- end
join tickets -- now join tickets
on tickets.trafficDateHour between t1.DateHour and t2.DateHour
I didn't make sample tables, this will not run as is, but something like this should do it for you:
SELECT *
FROM tickets, trips
WHERE
trips.datehour in (
SELECT trips.datehour
FROM tickets, trips
WHERE
tickets.ticket_date < trips.datehour AND
trips.checkin = 0
) AND
tickets.ticket_date > trips.datehour AND
trips.checkin = 1
If you are running this for a specific date as described in the comment above, it will work. If you are trying to run it for a set of ticket dates all at once, you'll require recursion. Recursion is a different beast depending on your flavor of SQL.

Joining a table to two one-to-many relationship tables in SQL Server

Happy Friday folks,
I'm trying to write an SSRS report displaying data from three (actually about 12, but only three relevant) tables that have akward relationships and the SQL query behind the data is proving difficult.
There are three entities involved - a Purchase Order, a Sales Order, and a Delivery. The problem is the a Purchase Order can have many sales orders, and also many deliveries which are NOT linked to the sales orders...that would be too easy.
Both the Sales Order and Delivery tables can be linked to the Purchase Order table by foreign keys and an intermediate table each.
I need to basically list Purchase Orders, a list of sales orders and a list of deliveries next to them, with NULLs for any fields that aren't valid so that'll give the required output in SSRS/when read by a human, ie, for a purchase order with 2 sales orders and 4 delivery dates;
PO SO Delivery
1234 ABC 05/10
1234 DEF 09/10
1234 NULL 10/12
1234 NULL 14/12
The above (when grouped by PO) will tell the users there are two sales orders and four (unlinked) delivery dates.
Likewise if there are more SOs than deliveries, we need NULLs in the Delivery column;
PO SO Delivery
1234 ABC 03/08
1234 DEF NULL
1234 GHI NULL
1234 JKL NULL
Above would be the case with 4 SOs and one delivery date.
Using Left Outer joins alone gives too much duplication - in this case 8 rows, as it gives 4 delivery dates for each match on the sales order;
PO SO Delivery
1234 ABC 05/10
1234 ABC 09/10
1234 ABC 10/12
1234 ABC 14/12
1234 DEF 05/10
1234 DEF 09/10
1234 DEF 10/12
1234 DEF 14/12
It's fine that the PO column is duplicated as SSRS can visually group that - but the SO/Delivery fields can't be allowed to duplicate as this can't be got rid of in the report - if I group the column in SSRS by SO then it still spits out 4 delivery dates for each one.
The only situation our query works nice is when there is just one SO per PO. In that case the single PO and SO numbers are duplicated together for x deliveries and can both be neatly grouped in SSRS. Unfortunately this is a rare occurence in the data.
I've thought of trying to use some sort of windowing function or CROSS APPLY but both fall down as they will repeat for every PO number listed and end up spitting out too much data.
At the point of thinking this just isn't set-based enough to be doable in SQL, I know the data is horrible..
Any help much appreciated.
EDIT - basical sqlfiddle link to the table schemas. Omitted many columns which aren't relevant. http://sqlfiddle.com/#!2/5ba16
Example data...
Purchase Order
PO_Number Style
1001 Black work boots
1002 Green hat
1006 Red Scarf
Sales Order
Sales_order_number PO_number Qty Retailer
A100-21 1001 15 Walmart
A100-22 1001 29 Walmart
A200-31 1006 1000 Asda
Delivery
Delivery_ID Delivery_Date PO_number
1543285 10/05/2014 1001
1543286 12/05/2014 1001
1543287 17/05/2014 1001
1543288 21/05/2014 1002
If you assign row numbers to the elements in salesorders and deliveries, you can link on that.
Something like this
declare #salesorders table (po int, so varchar(10))
declare #deliveries table (po int, delivery date)
declare #purchaseorders table (po int)
insert #purchaseorders values (123),(456)
insert #salesorders values (123,'a'),(123,'b'),(456,'c')
insert #deliveries values (123,'2014-1-1'),(456,'2014-2-1'),(456,'2014-2-1')
select *
from
(
select numbers.number, p.po, so.so, d.delivery from #purchaseorders p
cross join (Select number from master..spt_values where type='p') numbers
left join (select *,ROW_NUMBER() over (partition by po order by so) sor from #salesorders ) so
on p.po = so.po and numbers.number = so.sor
left join (select * , ROW_NUMBER() over (partition by po order by delivery) dor from #deliveries) d
on p.po = d.po and numbers.number = d.dor
) v
where so is not null or delivery is not null
order by po,number

Get the row with the max date value with criteria - access 2007/2010

My main table, from which I take all the data from is "RequestTable" (I reduced it down to make it easier) in which I have:
ID_student
ID_professor
Date (and the three altogether are primary keys)
changeprofessor-note - if student wants to change the professor
then he/she should write in that field a sentence
why he/she wants to do the change
professor-reject-note - if the professor is not happy about the work of
the student, then he can choose not to mentor that
student anymore, leaving him without a mentor and the
student should choose another mentor later.
ID-seminar- after choosing a mentor the students
can choose the seminar they want to work on
changeofSeminar-note - if the student wants to change the seminar
then they need to write the reason why in here
(then the ID of the new seminar should be written in
the ID seminar field also)
IDapprove-reject - all approving or rejecting is going through this field
My initial theory was that the students could choose the mentor and the seminar in one row, but it seems too complicated now because I have no idea how to make everything work after changing mentors, declined mentoring, changing seminars and so on.
I set a more comfortable theory that all the students need to choose the mentor first. So that I could get easier the data of mentoring when needed. And I set "is null" in the query under the "ID_seminar" and "changeofseminar-note" because any changes on just the seminar part can't affect the rows where the students chosen their mentors/professors and got approved.
I implemented your code and got this:
SELECT [requesttable].ID_Student, Max([requesttable].Datum) AS MaxOfDatum, First([requesttable].ID_Profesor) AS ID_Profesor, [requesttable].ID_status_odobrenja
FROM [requesttable]
WHERE ((([requesttable].ID_Student) Not In (SELECT [ID_Student]
FROM [requesttable]
WHERE [IDapprove-reject] IS NOT NULL )))
GROUP BY [requesttable].ID_Student, [requesttable].IDapprove-reject, [requesttable].changeseminar-note, [requesttable].ID_seminar
HAVING ((([requesttable].IDapprovereject)=1) AND (([requesttable].changeseminar-note) Is Null) AND (([requesttable].Id_seminar) Is Null))
ORDER BY [requesttable].ID_Student, Max([requesttable].Datum), First([requesttable].ID_Profesor), [requesttable].IDapproved-reject;
And i get:
3 12 1
15 11 1
55 5 1
And I need:
3 6 1
15 6 1
52 5 1 - after being rejected by mentor 10,
the student choose another mentor (id 5) and got approved.
55 5 1
Old info below:
I got my query to this point and two other data are set to show only rows with null values to get this:
ID student Id professor date professor-reject-note ID accept/reject
3 12 12.11.2012 null 1
3 6 13.11.2012 null 1
52 10 12.11.2012 null 1
52 10 15.11.2012 NOT null 1
55 5 12.11.2012 null 1
I want my results to be
3 6 12.10.2013 null 1
15 6 7.1.2013 null 1
55 5 12.11.2012 null 1
Totally exclude StudentID 52 because of the professor-reject-note meaning the professor doesn't want to mentor the student anymore. Also I have a doubt about the ID accept/reject number in that option , maybe I could set it to 2 instead of 1 to make it easier. 1 means accepted, 2 would mean rejected, but if I set it to 2 and exclude the entire row I still can't get rid of the other ID 52 row. I'm a bit confused about it and have no clue how make it work.
If I set date to maxdate and Id professor to group by FIRST I almost get what I want, all the data is right except the Student ID 52 is still there - both rows.
You could use:
SELECT t.[id student],
t.[id professor],
t.DATE,
t.[professor-reject-note],
t.[id accept/reject]
FROM atable t
WHERE t.[id student] NOT IN
(SELECT [id student]
FROM atable
WHERE [professor-reject-note] IS NOT NULL)
Your field / column names could do with some work.