invalid column name in HAVING [duplicate] - sql

This question already has answers here:
Why can't I use an alias for an aggregate in a having clause?
(8 answers)
Closed 4 years ago.
Anyone have an idea why the 'CoID' value isnt recognized?
select
ac.AccountID
, max(case when c.Name = 'email' then c.Data end) as Email
, max(case when c.Name = 'phone' then c.Data end) as Phone
, max(a.CompanyID) as CoID
from paul_AccountContacts ac
left join paul_Contact c on c.ID = ac.ContactID
left join paul_Account a on a.ID = ac.AccountID
having (CoID in (1506)) --ERROR HERE
order by ac.AccountID
Error:
Invalid column name 'CoID'.

If, you want to filter the single record then use where clause instead of having
select
ac.AccountID,
max(case when c.Name = 'email' then c.Data end) as Email,
max(case when c.Name = 'phone' then c.Data end) as Phone,
max(a.CompanyID) as CoID
from paul_AccountContacts ac
left join paul_Contact c on c.ID = ac.ContactID
left join paul_Account a on a.ID = ac.AccountID
group by ac.AccountID
where a.CompanyID = 1506 -- use IN clause whenever you have multiple CompanyID ids
order by ac.AccountID
However, having is used when you want to filter out after some aggregation or filter out based on aggregation or withing the aggregation
So. your query will look like with having clause :
select
ac.AccountID,
max(case when c.Name = 'email' then c.Data end) as Email,
max(case when c.Name = 'phone' then c.Data end) as Phone,
max(a.CompanyID) as CoID
from paul_AccountContacts ac
left join paul_Contact c on c.ID = ac.ContactID
left join paul_Account a on a.ID = ac.AccountID
group by ac.AccountID
having max(a.CompanyID) = 1506
order by ac.AccountID;

In logical query processing SELECT is executed after HAVING or WHERE clause clause. therefore it is not able to recognize column name created in SELECT. Try using max(a.CompanyID) instead:
select
ac.AccountID
, max(case when c.Name = 'email' then c.Data end) as Email
, max(case when c.Name = 'phone' then c.Data end) as Phone
, max(a.CompanyID) as CoID
from paul_AccountContacts ac
left join paul_Contact c on c.ID = ac.ContactID
left join paul_Account a on a.ID = ac.AccountID
Group by ac.AccountID
having max(a.CompanyID) = 1506 --ERROR HERE
order by ac.AccountID

You are missing the group by clause, which you obviously want since you are using aggregation functions. I assume from your select what you want is:
group by ac.AccountID
Afterwards, are you trying to take only CompanyIDs of 1506, or you want to only show grouping results for which max is 1506: The first is done like this:
Where a.CompanyID = 1506
the second like this
having max(a.CompanyID)=1506

Related

Pivoting duplicate data from rows to columns in SQL Server

I have a dataset with duplicate values due to number of feedback received for a particular candidate.
There are 3 feedback types are Security, Others and Social.
A candidate can have multiple of these feedback. And this feedback type name has to be taken by joining Org and orgtype table. But this gives me duplicate values in my result set.
The query with few columns is like this:
select
c.id as [Candidate ID]
,c.name as [Candidate Name]
,cf.status as [feedback status]
,e.name as [Feedback Type]
from
Candidates c
left join
Candidate_Feedback CF ON CF.CandidateId = c.ID
left join
Organizations d on CF.OrgId = d.ID
left join
OrganizationTypes e on d.OrganizationTypeId = e.Id
How can I pivot data where I need only one row for each candidate and feedback types in columns instead of rows? ( Such as col1 - Feedback_social, Col2 - Feedback_Other, col3 - Feedback_Security , col4 - 'N/A' (if feedback not present)
Due to other scenarios in my query I need these 3 columns for each candidate.
You could use using conditional aggregation, like:
select
c.id as [Candidate ID],
c.name as [Candidate Name]
max(case when e.name = 'Social' then cf.status end) [Feedback Social],
max(case when e.name = 'Other' then cf.status end) [Feedback Other],
max(case when e.name = 'Security' then cf.status end) [Feedback Security]
from Candidates c
left join Candidate_Feedback CF ON CF.CandidateId = c.ID
left join Organizations d on CF.OrgId = d.ID
left join OrganizationTypes e on d.OrganizationTypeId = e.Id
group by c.id, c.name

How do you turn string values into columns in unpivot statement?

Im trying write an unpivot query that turns User data stored in columns into one row per user. Heres what I have so far, thanks to other SO posts:
http://sqlfiddle.com/#!18/fa77c/2
Error (makes sense because "email" is a string stored in the Name column):
Invalid column name 'email'.
End goal:
ID Email Phone
1 a#a.com 111
2 b#b.com 222
3 NULL 333
Not sure that unpivot is really what you want here. You can do this pretty easily with some aggregation.
select ac.AccountID
, max(case when c.Name = 'email' then c.Data end) as Email
, max(case when c.Name = 'phone' then c.Data end) as Phone
from AccountContacts ac
left join Contacts c on c.ID = ac.ContactID
group by ac.AccountID
Please realize the reason your struggling here is because your data structure is an anti-pattern known as EAV (entity attribute value). It is a real pain to work with. If at all possible fixing your data structure would be a great plan.
Based on the desired results and the setup provided in your SQL Fiddle, you are looking to PIVOT your data, not UNPIVOT it.
This query does the trick:
with src as (
select accountid
, name
, data
from accountcontacts ac
join contacts c
on c.id = ac.contactid
)
select *
from src
pivot (max(data)
for name in ([email],[phone])) pvt
You can do conditional aggregation:
SELECT ac.AccountID as ID,
max(case when c.name = 'email' then c.data end) email,
max(case when c.name = 'phone' then c.data end) phone
from Contacts c
left join AccountContacts ac on ac.ContactID = c.id
group by ac.AccountID;
select a.data email, b.data phone from
(select * from Contacts a
join AccountContacts b on a.id=b.ContactID where name='email')a
full join (
select * from Contacts a
join AccountContacts b on a.id=b.ContactID where name='phone'
)b on a.AccountID=b.AccountID

SQL SELECT clause with WHERE statement for specific order

I have a through table, doctor_specialties that has a column ordinal that I would like to use in order to create a column named primary_specialty and also secondary_specialty. The logic for primary_specialty is WHERE ordinal == 1.
How can I add the primary_specialty and secondary_specialty columns? One approach would be to use a WHERE statement with the INNER JOIN but I think that would be less efficient?
SELECT pd.name AS "doctor_name",
s.name AS "primary_specialty" WHERE ds.ordinal == 1
FROM doctor_profiles AS dp
INNER JOIN doctor_specialties AS ds on dp.id = ds.doctor_profile_id
INNER JOIN specialties AS s on ds.specialty_id = s.id
Desired output is
name primary_specialty secondary_specialty
Josh Dermatology, Cosmetic Dermatology
Linda Primary Care null
You need to achive this by using case statement. example is shared below
SELECT pd.name AS "doctor_name",
case when ds.ordinal = 1 then s.name end as "primary_specialty",
case when ds.ordinal <> 1 then s.name end as "secondary_specialty"
FROM doctor_profiles AS dp
INNER JOIN doctor_specialties AS ds on dp.id = ds.doctor_profile_id
INNER JOIN specialties AS s on ds.specialty_id = s.id
You can use conditional aggregation:
SELECT dp.name AS "doctor_name",
MAX(CASE WHEN ds.ordinal = 1 THEN s.name END) AS "primary_specialty",
MAX(CASE WHEN ds.ordinal != 2 THEN s.name END) AS "secondary_specialty"
FROM doctor_profiles AS dp
INNER JOIN doctor_specialties AS ds on dp.id = ds.doctor_profile_id
INNER JOIN specialties AS s on ds.specialty_id = s.id
GROUP BY pd.name
You can alter the existing, or use additional MAX aggregates containing CASE expressions, in order to implement the logic for secondary specialties.

Selecting columns based on a case SQL

I'm wondering how I can return specific results depending on my first selected statement. Basically I have two IDs. CustBillToID and CustShipToID. If CustShipToID is not null I want to select that and all the records that are joined to it. If it is null default to the CustBillToID and all the results that are joined to that.
Here is my SQL that obviously doesn't work. I should mention I tried to do a sub query in the conditional, but since it returns multiple results it won't work. I am using SQL Server 2012.
SELECT CASE WHEN cp.CustShipToID IS NOT NULL
THEN
cy.CustDesc,
cy.Address1,
cy.Address2,
cy.City,
cy.State,
cy.ZIP,
cy.Phone
ELSE
c.CustDesc,
c.Address1,
c.Address2,
c.City,
c.State,
c.ZIP,
c.Phone
END
LoadID,
cp.CustPOID,
cp.POBillToRef,
cp.POShipToRef,
cp.CustBillToID,
cp.CustShipToID,
cp.ArrivalDate,
cp.LoadDate,
cp.StopNum,
cp.ConfNum,
cp.EVNum,
cp.ApptNum,
ld.CarrId,
ld.Temperature,
cr.CarrDesc
FROM [Sales].[dbo].[CustPO] AS cp
LEFT OUTER JOIN Load AS ld
ON cp.LoadID = ld.LoadID
LEFT OUTER JOIN Carrier AS cr
ON ld.CarrId = cr.CarrId
LEFT OUTER JOIN Customer AS c
ON c.CustId = cp.CustBillToID
WHERE CustPOID=5213
Any ideas?
Also my current SQL is below, I do a conditional to determine if it's set. I'd rather do it in SQL if possible.
SELECT cp.LoadID,
cp.CustPOID,
cp.POBillToRef,
cp.POShipToRef,
cp.CustBillToID,
cp.CustShipToID,
cp.ArrivalDate,
cp.LoadDate,
cp.StopNum,
cp.ConfNum,
cp.EVNum,
cp.ApptNum,
ld.CarrId,
ld.Temperature,
cr.CarrDesc,
c.CustDesc as CustBillToDesc,
c.Address1 as CustBillAddress1,
c.Address2 as CustBillAddress2,
c.City as CustBillCity,
c.State as CustBillState,
c.ZIP as CustBillZIP,
c.Phone as CustBillPhone,
cy.CustDesc as CustShipToDesc,
cy.Address1 as CustShipAddress1,
cy.Address2 as CustShipAddress2,
cy.City as CustShipCity,
cy.State as CustShipState,
cy.ZIP as CustShipZIP,
cy.Phone as CustShipPhone
FROM [Sales].[dbo].[CustPO] as cp
left outer join Load as ld
on cp.LoadID = ld.LoadID
left outer join Carrier as cr
on ld.CarrId = cr.CarrId
left outer join Customer as c
on c.CustId = cp.CustBillToID
left outer join Customer as cy
on cy.CustId = cp.CustShipToID
WHERE CustPOID=?
You need a separate case for each column:
SELECT (CASE WHEN cp.CustShipToID IS NOT NULL THEN cy.CustDesc ELSE c.CustDesc END) as CustDesc,
(CASE WHEN cp.CustShipToID IS NOT NULL THEN cy.Address1 ELSE c.Address1 END) as Address1,
(CASE WHEN cp.CustShipToID IS NOT NULL THEN cy.Address2 ELSE c.Address2 END) as Address2,
(CASE WHEN cp.CustShipToID IS NOT NULL THEN cy.City ELSE c.City END) as City,
(CASE WHEN cp.CustShipToID IS NOT NULL THEN cy.State ELSE c.State END) as State,
(CASE WHEN cp.CustShipToID IS NOT NULL THEN cy.ZIP ELSE c.ZIP END) as ZIP,
(CASE WHEN cp.CustShipToID IS NOT NULL THEN cy.Phone ELSE c.Phone END) as Phone,
. . .
For this, you basically want to build a string that is your SQL and then execute the string...look # the answer to this one ::
SQL conditional SELECT
Did you try coalesce(CustShipToID,CustBillToID ) ?
...
FROM [Sales].[dbo].[CustPO] as cp
left outer join Load as ld
on cp.LoadID = ld.LoadID
left outer join Carrier as cr
on ld.CarrId = cr.CarrId
inner join Customer as c
on c.CustId = coalesce(cp.CustShipToID,cp.CustBillToID )
...

Trying to pull new SQL objects between dates from another table

I have a query that pulls the information from the sys tables to produce a list of the new objects that have been created in our environment. What I need to do is the last part of the query needs to be able to look at another table and pull the 3 most current records and use the date from those records to show only the objects that have been created since those 3 dates. In my testing I keep running into "Subquery returned more than 1 value" Any help would be greatly appreciated.
EDIT:
I am currently running SQL 2008 R2.
The query runs now as is, but only pulls the most recent date, I need it to pull everything from the last 3 dates.
SELECT
a.name AS ObjectName, b.name AS ParameterName, c.name AS DataType,
b.isnullable AS [Allow Nulls?], a.crdate AS CreateDate,
CASE WHEN d .name IS NULL THEN 0 ELSE 1 END AS [PKey?],
CASE WHEN e.parent_object_id IS NULL THEN 0 ELSE 1 END AS [FKey?],
CASE WHEN e.parent_object_id IS NULL THEN '-' ELSE g.name END AS [Ref Table],
CASE WHEN h.value IS NULL THEN '-' ELSE h.value END AS Description,
c.length AS FieldSize, a.replinfo AS IsReplicated,
CASE a.xtype WHEN 'V' THEN 'View' WHEN 'P' THEN 'StoredProcedure' WHEN 'FN' THEN 'ScalarFunction' WHEN 'F' THEN 'ForeignKey' WHEN 'U' THEN 'Table' WHEN
'TR' THEN 'Trigger' WHEN 'TT' THEN 'TableType' WHEN 'PK' THEN 'PrimaryKey' END AS ObjectType
FROM
sys.sysobjects AS a
INNER JOIN sys.syscolumns AS b ON a.id = b.id
INNER JOIN sys.systypes AS c ON b.xtype = c.xtype
LEFT OUTER JOIN
(SELECT so.id, sc.colid, sc.name
FROM
sys.syscolumns AS sc
INNER JOIN sys.sysobjects AS so ON so.id = sc.id
INNER JOIN sys.sysindexkeys AS si ON so.id = si.id AND sc.colid = si.colid
WHERE (si.indid = 1)) AS d ON a.id = d.id AND b.colid = d.colid
LEFT OUTER JOIN sys.foreign_key_columns AS e ON a.id = e.parent_object_id AND b.colid = e.parent_column_id
LEFT OUTER JOIN sys.objects AS g ON e.referenced_object_id = g.object_id
LEFT OUTER JOIN sys.extended_properties AS h ON a.id = h.major_id AND b.colid = h.minor_id
WHERE (a.type = 'U' OR
a.type = 'V' OR
a.type = 'F' OR
a.type = 'PK' OR
a.type = 'P' OR
a.type = 'FN' OR
a.type = 'TT' OR
a.type = 'TR') AND
(a.crdate >
(SELECT TOP (1) DeployDate
FROM OtherTable.dbo.Tracking
ORDER BY DeployDate DESC))
ORDER BY CreateDate DESC
If I'm understanding your question correctly, you want the minimum of the last 3 dates in the tracking table.
If so, you could do something like this...
(a.crdate >
(SELECT Min(topthree.DeployDate)
FROM (select Top 3 DeployDate
From dbo.Tracking
ORDER BY DeployDate DESC
) as topthree
)
)