SQL getting multiple columns into one column - sql-server

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.

Related

Find third largest quote ever created for each of the accounts in the EC1 area

Can anyone help I'm new to SQL and trying to figure out the below question see image for the table structure;
Question = Select account name, contact last name, case number, quote number, quote date and quote value for the f third-largest quote ever created for each of the accounts in the EC1 area
So far I got;
Select
a.accountname, cc.lastname, c.casenumber,
q.quotenumber, q.quotedate, q.quotevalue
from
TBL_Quote q
Left join
TBL_case c On q.caseid = c.caseid
Left join
tbl_contact cc On c.contactID = cc. contactID
Left join
tbl_account a On a.accountid = cc.accountid
Where
left(a.postcode, 3) like 'EC1'
and for the third:
SELECT TOP 1 value
FROM
(SELECT DISTINCT TOP 3 value
FROM tbl_quote
ORDER BY value DESC) a
ORDER BY value
I can't seem to combine the top 3 and the query is it best to overpartion by ?
I would suggest joins and a row-limiting clause:
select ac.accountName, co.lastName, ca.caseNumber, qu.quoteNumber
from tbl_account ac
inner join tbl_contact co on co.accountId = ac.accountId
inner join tbl_case ca on ca.contactId = co.contactId
inner join tbl_quote qu on qu.caseId = ca.quoteId
where ac.postcode like 'EC1%'
order by len(qu.value) desc
offset 2 rows fetch next 1 row only

Answer me with the update statement

I have 101 records. and the prefix is a column.
[Insurance phone] is like 589-652-5256 and I have mistakenly done like
Substring ( EE.[Insurance Phone],4,3)
which result as -65 and I wanted it as 652 So I am writing update statement for same. I have different different phone number for 101 records.
When I do a select statement I got each row's middle 3 number right, but if I use update statement then 625 [1st record of 101 record] number gets updated to all 101 records.
Below are the select and update statements.
select
IC.COMPANY_NAME, EE.[Insurance Name],
substring (EE.[Insurance Phone], 5, 3) as phone
from
Insurance_Companies_28012020 EE
join
INS_COMPANIES IC on EE.[Insurance Name] = IC.COMPANY_NAME
where
ic.IS_ACTIVE = 1
and [Insurance Phone] is not null
update BUSINESS_PHONE_NUMBERS
set PREFIX = substring(EE.[Insurance Phone], 5, 3)
from Insurance_Companies_28012020 EE
join INS_COMPANIES IC on EE.[Insurance Name] = IC.COMPANY_NAME
where ic.IS_ACTIVE = 1 and [Insurance Phone] is not null
I guess your query should look like:
update t1
set t1.PREFIX = substring(EE.[Insurance Phone], 5, 3)
from BUSINESS_PHONE_NUMBERS t1
inner join Insurance_Companies_28012020 EE on ee.KEY_COLUMN = t1.KEY_COLUMN -- here you should link these two tables
inner join INS_COMPANIES IC on EE.[Insurance Name] = IC.COMPANY_NAME
where ic.IS_ACTIVE = 1
and [Insurance Phone] is not null
Edited:
Your update statement doesn't link tables BUSINESS_PHONE_NUMBERS and Insurance_Companies_28012020, which produces a Cartesian product in the execution plan, which leads to the strange behaviour of the end update result. You get the situation: you need to update N rows, but you have N*M rows with all possible combinations. It's hard to say which one of them will be selected to update. In your case, it is the first record from Insurance_Companies_28012020 table.
Also, in your UPDATE statement you can add the condition to WHERE clause: BUSINESS_PHONE_NUMBERS.KEY_COLUMN = EE.KEY_COLUMN. This should give the same result as in my UPDATE.
3o...,
Thanks for your answer it helped me to get to the exact destination.
As I said there is no relation between ee and t1. So I need to change your update statement as below. And it worked fine for me.
update t1
set t1.PREFIX = substring(EE.[Insurance Phone], 5, 3)
from BUSINESS_PHONE_NUMBERS t1
inner join INS_COMPANIES IC on IC.INS_COMPANY_ID = t1.OWNER_ID -- here you should link these two tables
inner join Insurance_Companies_28012020 EE on EE.[Insurance Name] = IC.COMPANY_NAME
where ic.IS_ACTIVE = 1
and [Insurance Phone] is not null

IS NULL being ignored

I am trying to run a query in T-SQL to pull back a data set based on a column being null.
This is a simplified version of the code:
SELECT
T1.Col1, T1.Col2,
T1.Col3, T1.Col4
FROM
table1 AS T1
INNER JOIN
table2 AS T2 ON T1.Col2 = T2.Col3
WHERE
T2.Col4 IS NULL
Problem is, the result includes rows where T2.Col4 are NULL and also not NULL, it's like the WHERE clause doesn't exist.
Any ideas would be greatly
UPDATE - full version of code:
SELECT
M.ref
,C.cname
,CL.clname
,C.ccity
,M.productLine
,M.code
,CL.date
,M.dept
,DPT.group
,TK2.tkname
,TK2.tkdept
FROM DB.dbo.manage AS M
OUTER JOIN DB.dbo.ClientManageRelationship AS CMR
ON CMR.RelatedEntityID = M.EntityID
OUTER JOIN DB.dbo.Client AS C
ON C.EntityID = CMR.EntityID
INNER JOIN DB.dbo.ManageCustomerRelationship AS MCR
ON MCR.EntityID = M.EntityID
INNER JOIN DB.dbo.Customer AS CL
ON CL.EntityID = MCR.RelatedID
INNER JOIN DB.dbo.timek AS TK
ON TK.tki = M.tkid
LEFT JOIN (SELECT Group = division, [Department] = newdesc, deptcode FROM DB.csrt.vw_rep_p_l_dept) AS DPT
ON tkdept = DPT.dept
LEFT JOIN (SELECT Name = TK2.tkfirst + ' ' + TK2.tklast, TK2.tki, TK2.dept, TK2.loc FROM DB.dbo.timek as TK2 WITH(NOLOCK)) AS TK2
ON TK2.tki = M.tkid
WHERE DPT.Department = 'Casualty'
AND UPPER (C.ClientName) LIKE '%LIMITED%'
AND CL.date > '31/12/2014'
AND CL.Date IS NULL
AND TK.tkloc = 'loc1' OR TK.tkloc = 'loc2'
ORDER BY M.ref
My first answer would be because you're using INNER JOIN. This only returns matches between the 2 tables. TRY FULL OUTER JOIN which will return all values regardless of matches and will include NULLS.
If you were looking to return all rows regardless of matches including NULLS from only one of the tables then use RIGHT or LEFT JOIN.
Say i had 2 tables ('Person' and 'Figure'). Not every person may have entered a figure on any one day. But an example may be i want to return all people regardless of whether they entered a figure or not on a certain day.
My initial approach to this would be a LEFT join because i want to return of all the people(left table) regardless of there being any matches in the figure table(right table)
FROM Person P
LEFT JOIN Figure F
ON P.ID = F.ID
This would produce a result such as
Name Figure
Sam 20
Ben 30
Matt NULL
Simon NULL
Whereas,
An inner join would produce only matching values not including nulls
Name Figure
Sam 20
Ben 30
Left join works the same way as right join but in the opposite direction. This is most likely the problem you were facing. But i hope this helped
I think the problem is in the last part of the where condition.
You should use brackets.
`WHERE DPT.Department = 'Casualty'
AND UPPER (C.ClientName) LIKE '%LIMITED%'
AND CL.date > '31/12/2014'
AND CL.Date IS NULL
AND (TK.tkloc = 'loc1' OR TK.tkloc = 'loc2')`
or
`WHERE DPT.Department = 'Casualty'
AND UPPER (C.ClientName) LIKE '%LIMITED%'
AND CL.date > '31/12/2014'
AND CL.Date IS NULL
AND TK.tkloc IN ('loc1', 'loc2')`

Extract Specific Data After a aggregation (Or any other solution for the desired result)

I want to select the Total "sales" of a specific "main_category" for the year 2016
(main categories that don't have sales in that year should appear as zero)
I have managed to select the "sales" of a specific "main category" with all the other "main_categories" (that doesn't have any sales) appearing as zero using below query:
SELECT
mc.name,
ISNULL(SUM(s.no_of_units * b.unit_price),0) AS tCatSales
FROM Sales s
INNER JOIN Invoice i ON i.invoice_ID = s.invoice_id
INNER JOIN Inventory inv ON inv.inventory_ID = s.inventory_ID
INNER JOIN Batch b ON b.batch_ID = inv.batch_ID
INNER JOIN Products p ON p.product_id = b.product_ID
INNER JOIN Category c ON c.category_ID = p.category_id
RIGHT JOIN Main_Category mc ON mc.cat_id = c.main_category
--WHERE YEAR(i.trans_date) = 2016
GROUP BY mc.name
--HAVING YEAR(i.trans_date)=2016
but when I try to further segregate it for year 2016 ONLY either by WHERE clause or HAVING clause, it stops showing "main_category" names that have zero sales in the year.
One thing that I can think of is to give the query invoices only from 2016
which I tried to did by doing something like,
Replacing the line:
INNER JOIN Invoice i ON i.invoice_ID = s.invoice_id
with:
INNER JOIN Invoice i ON i.invoice_ID IN (SELECT invoice_id FROM Invoice in2 WHERE Year(in2.trans_date)=2016)
which did display the categories with zero values but with increased the calculated Sales Amount (from 2069 to something 203151022.75).
I understand this addition is somewhat illogical and disrupts the whole Inner Joins but so far these are the closest thing I can think of or find on the web.
I REPEAT the desired result is: main categories that don't have sales in that year should appear as zero with the year given year/month/date
As Sean and Eli mentioned, RIGHT JOIN is not recommended, you may change it to LEFT JOIN, OR use subquery like this:
SELECT
mc.name,
tCatSales = ISNULL(
(
SELECT
SUM(s.no_of_units * b.unit_price) AS tCatSales
FROM Sales s
INNER JOIN Invoice i ON i.invoice_ID = s.invoice_id
INNER JOIN Inventory inv ON inv.inventory_ID = s.inventory_ID
INNER JOIN Batch b ON b.batch_ID = inv.batch_ID
INNER JOIN Products p ON p.product_id = b.product_ID
INNER JOIN Category c ON c.category_ID = p.category_id
WHERE mc.cat_id = c.main_category
AND YEAR(i.trans_date) = 2016
) , 0)
FROM Main_Category mc
try this:
WHERE ISNULL(YEAR(i.trans_date), 1) = 2016
if you put simple equals conditions on outer join it will eliminate nulls, which give zero-valued rows you desire.
Also note that something like:
WHERE YEAR(i.trans_date) = 2016
is not sargable, see here

SQL Server Nearest Date

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

Resources