SQL Server Nearest Date - sql-server

I have a table of persons and a table of addresses. The persons table contains one instance of the person and the addresses table can contain multiple instances of a person. The persons table contains a date added field and the addresses table also contains a date added field. How do I find the nearest date before or on in the addresses table and then match that to the persons table so only one address is returned?
This is the SQL I have to do a basic join for an exact match:
select p.personid,a.addressid,p.dateadded,a.dateadded
from persons p inner join a
on p.personid = a.personid and
p.dateadded = a.dateadded
Edit: here are the persons table and addresses table:
personid dateadded
1 01/01/2015
2 30/09/2014
3 01/08/2015
addressid personid dateadded
1 1 01/01/2015
2 2 16/02/2013
3 2 03/12/2013
4 3 19/05/2012
5 3 30/07/2015
6 3 07/09/2015
And here is the output I'm trying for:
personid addressid dateadded_person dateadded_address
1 1 01/01/2015 01/01/2015
2 3 30/09/2014 03/12/2013
3 6 01/08/2015 30/07/2015
Thanks

This is the general approach that doesn't involve top or analytic functions.
Based on your comment I edited to change the logic that finds which address is the most relevant. This version takes the person then joins to addresses and takes only those addresses added before the person was. The person_id and maximum (latest) dateadded are captured. After that it's just another join again to addresses to pick up the single row you're interested in.
select ...
from
persons as p
inner join
(
select personid, max(a.dateadded) as dateadded
from person as p inner join addresses as a on a.personid = p.personid
where a.dateadded <= p.dateadded
group by person
) as most_recent
on most_recent.personid = p.personid
inner join address as a
on a.personid = p.person_id and a.dateadded = most_recent.dateadded
You might also like cross apply:
select ...
from
persons as p
cross apply (
select max(a.dateadded) as most_recent from addresses as a
where a.personid = p.personid and a.dateadded <= p.dateadded
) as mr
inner join address as a
on a.personid = p.person_id and a.dateadded = mr.most_recent

Related

Get element names

I got 4 tables as follows:
tbProjekt
--------------
Id
every Machine has ProjektId which belongs to:
tblMaszyna
--------------
Id
ProjektId
tblElement
--------------
Id
Name
in this table i am associating elements with machines:
tblMaszElem
--------------
Id
IdElem
IdMach
I would like to take those elements - Name from tblElement which belongs to machines which belongs to specified ProjectId. So lets say for ProjectId 10 How can i achieve that?
select e.Name
from tbElement e
inner join tbMaszElem me on me.IdElem = e.Id
inner join tbMaszyna m on m.Id = me.IdMach
inner join tbProject p on p.Id = m.ProjektId
where
p.Id = 10
This should do. This selects the Name column of all entries in the tbElement table which are associated to a machine that's associated to a project where the project ID is 10.
Please check this sample and its comment
select
te.name
from
tblMaszElem tmem
inner join tblElement te on te.id = tmem.IdElem
inner join tblMaszyna tmzy on tmzy.id = tmem.IdMach
--inner join tbProjekt tp on tp.id = tmzy.ProjektId --i think this should be avoidable
where
tp.id = 10

employee attendance structure using 3 table left innerjoins

SELECT b.Device_Person_ID, a.Personal_id, Date1,
CASE WHEN b.Device_Person_id IS NOT NULL THEN 'A' ELSE 'P' END as Emp_Status
FROM Emp_setting a
LEFT OUTER JOIN (SELECT device_person_id, MAX(logDateTime) AS Date1 FROM tempDeviceLogs
GROUP BY device_person_id) b
ON a.personal_id = b.device_person_id
here i joined only two tables but i need to join 3 tables to show employee details with attendance with specific date.
Required output is
employeename device_person_id designation emptype status
'dbo.persons_profile table
[pesonal_id]
,[Emp_Code]
,[Title]
,[First_name]
,[Middle_name]
,[last_name]
,[Father_Husband_Name]
,[Dob]
,[Age]
,[gender]
,[Marital_status]
,[Nationality]
,[bloodGroup]
,[perAddress]
,[PerStreet]
,[PerLocation]
,[PerCity]
,[PerPincode]
,[CorAddress]
,[CorStreet]
,[CorLocation]
,[CorCity]
,[CorPincode]
,[LandlinePhone]
,[cellNo]
,[EmailId]
,[NosofDependendants]
,[Dependendants_details]
,[Emergency_FirstName]
,[Emergency_Middle_name]
,[Emergency_Last_name]
,[Emergency_WithRelation]
,[Emergency_PhoneNo]
,[Emergency_CellNo]
,[Emergency_emailId]
,[Office_PF_ac_no]
,[ESI_ac_no]
,[JoinedDate]
,[Photofile]
,[ReportTo]
,[Brief_Notes]
,[dateofTermination]
,[termination_note]
,[Print_Priority]
,[DeviceEmployeeID]
,[LogsPermitted]
,[Machin_install_id]
,[Designation]
,[Dept]
,[Section]
,[Groups]
,[EmpWorkingTypeT]'
dbo.tempDeviceLogs table
[LogsID]
,[Device_Person_id]
,[Device_id]
,[logDateTime]
,[logVerifyMode]
,[workCodeID]
,[Machin_install_id]
,[data_loaded_dt]
,[Inout]
dbo.Emp_setting table
[Empset_id]
,[personal_id]
,[DesignationID]
,[DivisionID]
,[Emp_status]
,[Emp_TypeId]
,[Dept_Id]
,[Group_Id]
,[NDIVGRP_CODE]
you need to write to join conditions for achieving what you required.
as an example:
SELECT a.<respective>_id, d.<respective>_title, s.<respective>_id
FROM Emp_setting a
INNER JOIN persons_profile s ON a.<respective>_id = s.<respective>_id
INNER JOIN Emp_setting d ON a.<respective>_id = d.<respective>_id
INNER JOIN tempDeviceLogs b ON s.<respective>_id = b.<respective>_id

Merge two returned rows in to one in t-sql

I have the following query which returns a result with person details. I have 2 person types in the database as OWNER and CONTACT. I only need name and address from the OWNER and the email from the CONTACT.
I have written the following query and please check.
SELECT DISTINCT
s.[id],
s.[entity_id],
s.[transfered],
s.[meta],
s.[operation]
c.[Company_Name] as CompanyName,
ISNULL(pa.HouseName,'') as HouseName,
ISNULL(pa.Street,'') as Street,
ISNULL(pa.Town,'') as Town,
ISNULL(pa.City,'') as City,
ISNULL(pa.County,'') as County,
ISNULL(pa.Country,'') as Country,
ISNULL(pa.Postcode,'') as PostCode,
ISNULL(p.Title,'') as Title,
ISNULL(p.Forenames,'') as FirstName,
ISNULL(p.Surname,'') as LastName,
ISNULL(pc.Email,'') as Email
FROM
dbo.COMPANY c INNER JOIN dbo.Sage_Company_Sync s ON s.entity_id = c.Company_ID
INNER JOIN dbo.CompanyPostalAddress cpa ON c.Company_ID = cpa.CompanyId
INNER JOIN dbo.POSTAL_ADDRESS pa ON cpa.PostalAddressId = pa.Address_ID
LEFT JOIN dbo.CompanyPerson cp ON cp.CompanyId = c.company_id
INNER JOIN dbo.PERSON p ON p.Person_ID = cp.PersonId
INNER JOIN dbo.PERSON pc ON pc.Person_ID = cp.PersonId
WHERE
cpa.PostalAddressTypeId = 2
and cp.PersonTypeId in (1,2)
and s.transfered = 0
and s.entity_type = 'company'
ORDER BY
s.operation ASC
The above query returns 2 rows. One for OWNER and one for COMPANY. My question is how can I make my query only return name and address details for OWNER and email for CONTACT in one row?
Update
Results
id entity_id transfered meta operation CompanyName HouseName Street Town City County Country PostCode Title FirstName LastName Email PersonType
5 25 0 U Testing 2 (home Address) Stoneleigh Epsom Surrey Uk KTJ19 0PQ Mr Dfdf Dfdf test#gmail.com CONTACT
5 25 0 U Testing 2 (home Address) Stoneleigh Epsom Surrey Uk KT1J9 0PQ Mr Testing Testing OWNER
Desired Results
id entity_id transfered meta operation CompanyName HouseName Street Town City County Country PostCode Title FirstName LastName Email PersonType
5 25 0 U Testing 2 Westways (home Address) Stoneleigh Ezpsofm Surrey Uk KT19 Q Mr Testing Testing test#gmail.com OWNER
The email is actually CONTACT persons email.
Thanks,
You can store the result of your query in a temporary table or a Common Table Expression and build another query on that.
Consider the following example:
DECLARE #tbl TABLE (ID INT, Name VARCHAR(20), Email VARCHAR(20), Relation VARCHAR(20))
INSERT INTO #tbl VALUES (1, 'Somebody', 'xyz#domain.com', 'Employer'), (1, 'Someone', 'Test', 'Employee');
WITH cte1 AS
(
SELECT * FROM #tbl WHERE Relation = 'Employer'
),
cte2 AS
(
SELECT t.ID, t.Name, c.Email, t.Relation FROM cte1 c INNER JOIN #tbl t ON c.ID = t.ID WHERE t.relation = 'Employee'
)
SELECT* FROM cte2
You can also check out this SQLFiddle example:
SQLFiddle

Count Function with Linked Table

I am attempting to count the number of parts ordered by each of our customers in 2013. However the returned results seem to be grouped by order number and not trader. I am using the following select statement;
SELECT orders.traderid, COUNT(orderitems.partid) AS configuredparts
FROM orders LEFT JOIN orderitems
ON orders.id = orderitems.orderid AND orders.ordertype = orderitems.ordertype
WHERE (orderitems.partid LIKE N'P%') AND (YEAR(orders.createddate) = 2013)
GROUP BY orders.traderid, orderitems.partid, orders.ordertype
HAVING (orders.ordertype = N'SO')
ORDER BY orders.traderid
an example of my results are
traderid configured parts
800001 3
800001 3
800001 2
800001 1
A00002 1
A00002 2
Any help is much appreciated
This answer gives the total number (count) of part ids in table [order items] by trader id in table [orders].
trader_id part_id total_parts
800001 P001 3
800001 P002 2
A00002 P001 1
If table [order items] has a qty column, you should change COUNT(oi.partid) to SUM(oi.qty).
SELECT
o.traderid as trader_id,
oi.partid as part_id,
COUNT(oi.partid) AS total_parts
FROM
orders as o
LEFT JOIN
orderitems as oi
ON
o.id = oi.orderid AND
o.ordertype = oi.ordertype
WHERE
(oi.partid LIKE N'P%') AND
(o.createddate >= '20130101') AND
(o.createddate < '20140101') AND
(o.ordertype = N'SO')
GROUP BY
o.traderid, oi.partid
ORDER BY
o.traderid, oi.partid
Last but not least, why does the caption have a linked table (server)?
If you are using a linked server you will need to use 4 part notation.
Linked_Server_Name.Database_Name.Schema_Name.Object_Name
give this a go
SELECT orders.traderid, COUNT(orderitems.partid) AS configuredparts
FROM orders LEFT JOIN orderitems AND orders.ordertype = orderitems.ordertype
ON orders.id = orderitems.orderid
WHERE (orderitems.partid LIKE N'P%') AND (orders.createddate >= '20130101')
AND (orders.createddate < '20140101') AND (orders.ordertype = N'SO')
GROUP BY orders.traderid
ORDER BY orders.traderid

SQL getting multiple columns into one column

I have this query that I am using to create a report:
SELECT DISTINCT
Appointment.ActivityId, Appointment.ScheduledStart, Appointment.OwnerIdName, Contact.AccountIdName, Appointment.new_ContactPersonName,
Appointment.Subject, Appointment.new_ColderNotes, Account.AccountId, Contact_1.ContactId, SystemUser.SystemUserId, SystemUser.FullName AS OptionalOwner,
Contact_1.FullName AS OptionalContact, Account.Name AS OptionalAccount, ActivityParty.PartyId, ActivityParty.ParticipationTypeMask,
Contact_1.FullName AS RequiredContact, Account.Name AS RequiredAccount, SystemUser.FullName AS RequiredOwner, Account.new_BusinessUnit
FROM
Contact AS Contact_1 RIGHT OUTER JOIN
Account RIGHT OUTER JOIN
SystemUser RIGHT OUTER JOIN
Appointment INNER JOIN
ActivityParty ON Appointment.ActivityId = ActivityParty.ActivityId ON SystemUser.SystemUserId = ActivityParty.PartyId ON Account.AccountId = ActivityParty.PartyId ON
Contact_1.ContactId = ActivityParty.PartyId LEFT OUTER JOIN
Contact ON Appointment.new_ContactPerson = Contact.ContactId
This returns rows for each type of User. So one row for Required user and one row for Optional User etc.
This is a small sample of data:
ActivityId ScheduledStart OwnerIdName AccountIdName new_ContactPersonName Subject new_ColderNotes AccountId ContactId SystemUserId OptionalOwner OptionalContact OptionalAccount PartyId ParticipationTypeMask RequiredContact RequiredAccount RequiredOwner new_BusinessUnit
f9ca2637-580a-e111-b263-001ec928e97f 11/17/2011 3:00:00 PM Bob Schulze Surmodics Pharmaceuticals Jason Kennedy Sales Visit - Surmodics NULL NULL NULL f69530fc-da00-e111-b263-001ec928e97f Bob Schulze NULL NULL f69530fc-da00-e111-b263-001ec928e97f 7 NULL NULL Bob Schulze NULL
f9ca2637-580a-e111-b263-001ec928e97f 11/17/2011 3:00:00 PM Bob Schulze Surmodics Pharmaceuticals Jason Kennedy Sales Visit - Surmodics NULL NULL NULL f69530fc-da00-e111-b263-001ec928e97f Bob Schulze NULL NULL f69530fc-da00-e111-b263-001ec928e97f 9 NULL NULL Bob Schulze NULL
f9ca2637-580a-e111-b263-001ec928e97f 11/17/2011 3:00:00 PM Bob Schulze Surmodics Pharmaceuticals Jason Kennedy Sales Visit - Surmodics NULL c629fb14-1101-e111-b263-001ec928e97f NULL NULL NULL NULL Surmodics Pharmaceuticals c629fb14-1101-e111-b263-001ec928e97f 5 NULL Surmodics Pharmaceuticals NULL NULL
f9ca2637-580a-e111-b263-001ec928e97f 11/17/2011 3:00:00 PM Bob Schulze Surmodics Pharmaceuticals Jason Kennedy Sales Visit - Surmodics NULL c629fb14-1101-e111-b263-001ec928e97f NULL NULL NULL NULL Surmodics Pharmaceuticals c629fb14-1101-e111-b263-001ec928e97f 8 NULL Surmodics Pharmaceuticals NULL NULL
As you can see they are the same ActivityID but have different ParticipationTypeMasks those are the 5, 7 , 9.
Using all this when I create a report I get something like this:
It creates a row for each ParticipationMask. What I am trying to do is making it so if the ParticipationMask is 1, 2, or 3 those names go into the Sales column and if it is 4, 5, 6 it goes in the Other Column and only have one row per activity, not one per ParticipationMask. Anyway to do this? I'm stuck. I'm open to doing it in SQL or SSRS.
Thanks!
This is a shot in the dark because we don't really know your data, but if you like your existing query it may be possible to simply use it twice but with a where clause like so:
SELECT
-- Common stuff?
ISNULL(Sales.ActivityId, Other.ActivityId), ISNULL(Sales.ScheduledStart, Other.ScheduledStart), ISNULL(Sales.OwnerIdName, Other.OwnerIdName), ISNULL(Sales.AccountIdName, Other.AccountIdName), ISNULL(Sales.new_ContactPersonName, Other.new_ContactPersonName), ISNULL(Sales.[Subject], Other.[Subject]), ISNULL(Sales.new_ColderNotes, Other.new_ColderNotes)
-- Sales stuff?
, Sales.AccountId, Sales.ContactId, Sales.SystemUserId, Sales.OptionalOwner, Sales.OptionalContact, Sales.OptionalAccount, Sales.PartyId, Sales.ParticipationTypeMask, Sales.RequiredContact, Sales.RequiredAccount, Sales.RequiredOwner, Sales.new_BusinessUnit
-- Other Stuff?
, Other.AccountId, Other.ContactId, Other.SystemUserId, Other.OptionalOwner, Other.OptionalContact, Other.OptionalAccount, Other.PartyId, Other.ParticipationTypeMask, Other.RequiredContact, Other.RequiredAccount, Other.RequiredOwner, Other.new_BusinessUnit
FROM
(
SELECT DISTINCT
Appointment.ActivityId, Appointment.ScheduledStart, Appointment.OwnerIdName, Contact.AccountIdName, Appointment.new_ContactPersonName,
Appointment.Subject, Appointment.new_ColderNotes, Account.AccountId, Contact_1.ContactId, SystemUser.SystemUserId, SystemUser.FullName AS OptionalOwner,
Contact_1.FullName AS OptionalContact, Account.Name AS OptionalAccount, ActivityParty.PartyId, ActivityParty.ParticipationTypeMask,
Contact_1.FullName AS RequiredContact, Account.Name AS RequiredAccount, SystemUser.FullName AS RequiredOwner, Account.new_BusinessUnit
FROM
Contact AS Contact_1 RIGHT OUTER JOIN
Account RIGHT OUTER JOIN
SystemUser RIGHT OUTER JOIN
Appointment INNER JOIN
ActivityParty ON Appointment.ActivityId = ActivityParty.ActivityId ON SystemUser.SystemUserId = ActivityParty.PartyId ON Account.AccountId = ActivityParty.PartyId ON
Contact_1.ContactId = ActivityParty.PartyId LEFT OUTER JOIN
Contact ON Appointment.new_ContactPerson = Contact.ContactId
-- Limit this part to Sales?
WHERE ParticipationTypeMask BETWEEN 1 AND 3
) as Sales
FULL OUTER JOIN
(
SELECT DISTINCT
Appointment.ActivityId, Appointment.ScheduledStart, Appointment.OwnerIdName, Contact.AccountIdName, Appointment.new_ContactPersonName,
Appointment.Subject, Appointment.new_ColderNotes, Account.AccountId, Contact_1.ContactId, SystemUser.SystemUserId, SystemUser.FullName AS OptionalOwner,
Contact_1.FullName AS OptionalContact, Account.Name AS OptionalAccount, ActivityParty.PartyId, ActivityParty.ParticipationTypeMask,
Contact_1.FullName AS RequiredContact, Account.Name AS RequiredAccount, SystemUser.FullName AS RequiredOwner, Account.new_BusinessUnit
FROM
Contact AS Contact_1 RIGHT OUTER JOIN
Account RIGHT OUTER JOIN
SystemUser RIGHT OUTER JOIN
Appointment INNER JOIN
ActivityParty ON Appointment.ActivityId = ActivityParty.ActivityId ON SystemUser.SystemUserId = ActivityParty.PartyId ON Account.AccountId = ActivityParty.PartyId ON
Contact_1.ContactId = ActivityParty.PartyId LEFT OUTER JOIN
Contact ON Appointment.new_ContactPerson = Contact.ContactId
-- Limit this part to Other?
WHERE ParticipationTypeMask BETWEEN 4 AND 6
) AS Other ON Sales.ActivityId = Other.ActivityId -- More cols for join?
One way is to join to your table twice and add the ParticipationMask filters to the Join Clause. e.g
JOIN activityparty activityparty_sales
ON appointment.activityid = activityparty_sales.activityid
AND activityparty_sales.participationtypemask IN ( 1, 2, 3 )
And
JOIN activityparty activityparty_other
ON appointment.activityid = activityparty_other.activityid
AND activityparty_other.participationtypemask IN ( 4, 5, 6 )
This is somewhat complicated (for me at least) because you've got a bunch of Right joins with nested joins.
I converted them into more standard joins (but without an ability to test I could be wrong)
SELECT [your fields]
FROM appointment
INNER JOIN activityparty activityparty_sales
ON appointment.activityid = activityparty_sales.activityid
AND activityparty_sales.participationtypemask IN ( 1, 2, 3 )
LEFT JOIN systemuser systemuser_sales
ON systemuser_sales.systemuserid = activityparty_sales.partyid
LEFT JOIN ACCOUNT account_sales
ON account_sales.accountid = activityparty_sales.partyid
LEFT JOIN contact AS contact_1_sales
ON contact_1_sales.contactid = activityparty_sales.partyid
LEFT OUTER JOIN contact contact_sales
ON appointment.new_contactperson = contact_sales.contactid
INNER JOIN activityparty activityparty_other
ON appointment.activityid = activityparty_other.activityid
AND activityparty_other.participationtypemask IN ( 4, 5, 6 )
LEFT JOIN systemuser systemuser_other
ON systemuser_other.systemuserid = activityparty_other.partyid
LEFT JOIN ACCOUNT account_other
ON account_other.accountid = activityparty_other.partyid
LEFT JOIN contact AS contact_1_other
ON contact_1_other.contactid = activityparty_other.partyid
LEFT OUTER JOIN contact contact_other
ON appointment.new_contactperson = contact_other.contactid
Another thing that complicates this is that without knowing your data may have multiple values for Sales and Other
If that's the case then you'll need to add two CTEs that use Row_Number and then Add a where clause
WHERE
(CTE_sales.activityid is null
or CTE_other.activityid is null
or CTE_sales.RN = CTE_other.RN)
*Actually a CTE isn't a bad idea
If I am interpreting your question correctly it sounds like you need to use sub queries.
Write one sub query to find the “Sales” data for the activity and another to find your “Other” data for the activity. You can then join each one back to the base activity record. Note that depending on exactly how your data is structured the sub queries could be as simple as adding an AND clause to the join which filters it down to just the desired record types, and then repeating the join with different criteria.
This should work assuming there is only one of each record type for the activity, if you start running into situations where a sub query can return more than one row per activity then you may have problems with it breeding extra rows while it tries to match up all of the possible combinations.

Resources