How I get multi-part identifier could not be bound error - sql

I try to perform an update on the same table depending on conditions. I know I can do the updates one by one. I tried
IF EXISTS (SELECT *
FROM tblA a
INNER JOIN tblB b ON a.id = b.id
WHERE a.number = 1 AND a.code BETWEEN 1 AND 20)
BEGIN
UPDATE tblA
SET name = b.name, zipCode = 'J2J'
END
ELSE
BEGIN
UPDATE tblA
SET city = b.city
END
I get the error:
multi-part identifier b.name, b.city could not be bound.
I would like to make the code more readable. So, is there a way to do it? Need help please.

If you want to access another table in your update you have to join that table on, and alias your tables and then UPDATE the primary table alias.
Also you can carry out your entire update in one go using a CASE expression to conditionally update the required columns. Its always best to use set based operations rather than procedural within SQL Server.
UPDATE A SET
[Name] = CASE WHEN A.number = 1 AND A.code BETWEEN 1 AND 20 THEN B.[Name] ELSE A.[NAME] END
, ZipCode = CASE WHEN A.number = 1 AND A.code BETWEEN 1 AND 20 THEN 'J2J' ELSE A.ZipCode END
, City = CASE WHEN A.number = 1 AND A.code BETWEEN 1 AND 20 THEN A.City ELSE B.City END
FROM tblA AS A
INNER JOIN tblB AS B ON A.id = B.id;
Although as this repeats the same CASE logic 3 times I would be inclined to calculate it once in a CROSS APPLY e.g.
UPDATE A SET
[Name] = CASE WHEN X.ConditionMet = 1 THEN B.[Name] ELSE A.[NAME] END
, ZipCode = CASE WHEN X.ConditionMet = 1 THEN 'J2J' ELSE A.ZipCode END
, City = CASE WHEN X.ConditionMet = 1 THEN A.City ELSE B.City END
FROM tblA AS A
INNER JOIN tblB AS B ON A.id = B.id
CROSS APPLY (VALUES (CASE WHEN A.number = 1 AND A.code BETWEEN 1 AND 20 THEN 1 ELSE 0 END)) AS X (ConditionMet);
I highly recommend reading the official docs as there are many clear examples there.

Related

Subquery problem when using WHERE in secondary SELECT

I have problem with query.
SELECT
a.code,
b.codename,
(SELECT COUNT(b.IdNum)
FROM
(SELECT *
FROM NumTable
WHERE YEAR(DateOfPoint) = 2019)) AS CountNumber
FROM
NumTable b
JOIN
CodeTable a ON a.id = b.id
WHERE
a.SellYear IS NOT NULL
My First question is about CountNumber is it ok ? I need to count only those b.IdNum that have DateOfPoint = 2019. It should only be to this field not to any other in this query, thats why I didn't use it in the end in WHERE.
Second question is about CountNumber too becouse I still get error msg that I got there incorrect syntax I was looking for it for about hour and couldn't find it.
Thanks
not sure what you are trying to get here but I think group by is more logicly here
SELECT a.code
,b.codename
,Sum(case when b.DateOfPoint= 2019 then 1 else 0) as CountNumber
FROM NumTable b
JOIN CodeTable a ON a.id = b.id
WHERE a.SellYear IS NOT NULL
group by a.code,b.codename
you will get a row for each code and codename and the number of dateofPoint in 2019 that it have if there are none it will return 0
You can use this query. I think, it will work for you:
SELECT a.code,
b.codename
FROM
NumTable b
JOIN
CodeTable a ON a.id = b.id
JOIN
(SELECT *
FROM NumTable
WHERE YEAR(DateOfPoint) = 2019) c ON c.id = b.id
JOIN
(SELECT id, COUNT(b.IdNum) FROM c) d ON c.id = d.id
WHERE
a.SellYear IS NOT NULL
You can use a window function:
SELECT c.code, n.codename, c.cnt_2019
FROM (SELECT n.*,
SUM(CASE WHEN YEAR(DateOfPoint) = 2019 THEN 1 ELSE 0 END) as cnt_2019
FROM NumTable n
) n JOIN
CodeTable c
ON c.id = n.id
WHERE c.SellYear IS NOT NULL;
Note that I also changed the table aliases so they are abbreviations for the table names rather than arbitrary letters.

Select an ID where there is only one row and that row is a specific value

I have this query. There's a lot of joins because I am checking if an ID is linked to any of those tables.
Currently, this query shows me any ID's that are not linked to any of those tables. I would like to add to it so that it also shows any IDs that are linked to the d table, but only if there is only 1 row in the D table and the type in the D field is 'member'.
SELECT
c.ID,
c.location,
c.pb,
c.name,
c.surname
FROM c
LEFT JOIN l on c.rowno = l.rowno
LEFT JOIN d on c.rowno = d.rowno
LEFT JOIN t on c.rowno = t.rowno
LEFT JOIN cj ON (c.rowno = cj.rowno OR c.rowno = cj.rowno2)
LEFT JOIN dj ON c.rowno = d.rowno
LEFT JOIN lg ON c.rowno = lg.rowno
LEFT JOIN tj ON c.rowno = tj.rowno
WHERE
c.status != 'closed'
AND l.rowno IS NULL
AND d.rowno IS NULL
AND t.rowno IS NULL
AND cj.rowno IS NULL
AND dj.rowno IS NULL
AND lg.rowno IS NULL
AND tj.rowno IS NULL
My first thought is to just add
WHERE D.type = 'member'
But that gives me all IDs that have a row with D.type = member (they could have 10 rows with all different types, but as long as 1 of those has type = member it shows up). I want to see ID's that ONLY have d.type = member
I'm sorry if I'm wording this badly, I'm having trouble getting this straight in my head. Any help is appreciated!
I would use exists for all conditions except the one on the D table:
SELECT c.*
FROM c JOIN
(SELECT d.rownum, COUNT(*) as cnt,
SUM(CASE WHEN d.type = 'Member' THEN 1 ELSE 0 END) as num_members
FROM t
GROUP BY d.rownum
) d
ON c.rownum = d.rownum
WHERE c.status <> 'closed' AND
NOT EXISTS (SELECT 1 FROM t WHERE c.rowno = t.rowno) AND
NOT EXISTS (SELECT 1 FROM l WHERE c.rowno = l.rowno) AND
. . .
I find NOT EXISTS is easier to follow logically. I don't think there is a big performance difference between the two methods in SQL Server.

Sql query if is null

I need help to return a result if value exists or no.
UPDATED: The image show where I need help:
You can do it using CASE EXPRESSION with a LEFT JOIN .
I didn't fully understand the output you expect, but just add the columns you want:
#in_myvar = 11
select bt.username,at.postid,
CASE WHEN ct.userid is null the 0 else 1 end as c_ind
from A at
INNER JOIN B bt
ON (at.userid = bt.userid and bt.userid = #in_myvar)
LEFT JOIN C ct
ON(ct.userid = at.userid)
Use this query:
declare #in_myvar int = 11
select B.username,A.postid,(Case when C.postid = A.postid then 1 Else 0 end) as UserExists
from A inner join B on A.userID = B.UserID
Left Join C
On C.userID = A.userID
where B.userID = #in_myvar
You want a result row for each record in A. So select from A. The data from the other tables can be got with subqueries:
select
(select username from b where b.userid = a.userid) as username,
a.postid,
case when exists (select * from c where c.userid = a.userid) then 1 else 0 end as has_c
from a;
As B:A = 1:n, you can also join B, if you like that better:
select
b.username,
a.postid,
case when exists (select * from c where c.userid = a.userid) then 1 else 0 end as has_c
from a
join b on b.userid = a.userid;

Using a CTE within an UPDATE statement that is using a CASE

I have the following code:
WITH CTE_List AS
(
SELECT B.Chain_Code, B.State
FROM Company.dbo.Company_Master AS A
LEFT JOIN Company.dbo.StateChain_List AS B
ON A.State = B.State
AND A.Chain_Code = B.Chain_Code
)
UPDATE Company.dbo.Company_Master
SET MFN_Ind = (
CASE
WHEN (CTE_List.Chain_Code IS NULL) AND (CTE_List.State IS NULL) THEN 'N'
ELSE 'Y'
END
)
FROM CTE_List
The select statement in CTE_List returns values like this, but corresponding to every record in the Company_Master table:
Chain_Code | State
00992 | IL
NULL | NULL
NULL | NULL
00732 | MA
NULL | NULL
My ultimate goal is to update the MFN_Ind column in Company.dbo.Company_Master based off this CTE_List. If Chain_Code and State are populated, then MFN_Ind = 'Y'. If Chain_Code and State are NULL, then MFN_Ind = 'N'.
As it's set up now, the query updates the MFN_Ind column with everything set as 'Y' so clearly the the first portion of the CASE is not catching the NULL fields. Any tips on how I can fix this?
Thank you!
I think you're making this far more complex than it needs to be.
UPDATE m
SET m.MFN_Ind = CASE WHEN l.State IS NULL THEN 'N' ELSE 'Y' END
FROM Company.dbo.Company_Master AS m
LEFT OUTER JOIN Company.dbo.StateChain_List AS l
ON m.State = l.State
AND m.Chain_Code = l.Chain_Code;
Or maybe better to just split it up into 2 - update all the rows to N, then update the matching rows to Y:
UPDATE Company.dbo.Company_Master SET m.MFN_Ind = 'N';
UPDATE m SET m.MFN_Ind = 'Y'
FROM Company.dbo.Company_Master AS m
WHERE EXISTS (SELECT 1 FROM Company.dbo.StateChain_List AS l
WHERE m.State = l.State
AND m.Chain_Code = l.Chain_Code);
WITH CTE_List AS
(
SELECT B.Chain_Code, B.State
FROM Company.dbo.Company_Master AS A
LEFT OUTER JOIN Company.dbo.StateChain_List AS B
ON A.State = B.State
AND A.Chain_Code = B.Chain_Code
)
---UPDATE THE RECORD
UPDATE m
SET MFN_Ind = (
CASE WHEN (CTE.Chain_Code IS NULL) AND (CTE.State IS NULL) THEN 'N'
ELSE 'Y'
END
)
FROM CTE_List cte
INNER JOIN Company.dbo.Company_Master m
on m.State =cte.state

SQL Server Query Returning Same Row Twice

I seem to be having trouble with the following query. It basically works, but I have a case where it is returning one row from mc_WorkoutDetails twice!
Here's the original query:
ALTER PROCEDURE [dbo].[mc_Workouts_GetActivities]
#WorkoutID bigint
AS
BEGIN
SET NOCOUNT ON
SELECT d.ID, a.Description,
CASE WHEN Reps = 0 THEN NULL ELSE Reps END AS Reps,
CASE WHEN Sets = 0 THEN NULL ELSE Sets END AS Sets,
CASE WHEN Minutes = 0 THEN NULL ELSE Minutes END AS Minutes,
d.Comments, c.Name AS Category, a.CategoryID,
(CASE WHEN v.ActivityID IS NULL THEN 0 ELSE 1 END) AS HasVideo,
a.ID AS ActivityID
FROM mc_WorkoutDetails d
INNER JOIN mc_Activities a ON d.ActivityID = a.ID
INNER JOIN mc_Activities_Categories c ON a.CategoryID = c.ID
LEFT OUTER JOIN mc_TrainerVideos v ON a.ID = v.ActivityID
WHERE (d.WorkoutID = #WorkoutID)
ORDER BY SortOrder, a.Description
RETURN ##ERROR
END
Then I tried changing:
INNER JOIN mc_Activities a ON d.ActivityID = a.ID
INNER JOIN mc_Activities_Categories c ON a.CategoryID = c.ID
To:
LEFT OUTER JOIN mc_Activities a ON d.ActivityID = a.ID
LEFT OUTER JOIN mc_Activities_Categories c ON a.CategoryID = c.ID
But that didn't seem to help. I still get the duplicate row.
Can anyone see what's happening?
What you can do is add a join back to the same table using a group to weed out the duplicate rows.
So add this to your join after FROM mc_WorkoutDetails d:
inner join (select [columns you want to select], max(id) id
from mc_WorkoutDetails
group by [columns you want to select] ) q on q.id = d.id
Let me know if that makes sense. Basically you are doing a distinct and getting the max id so you eliminate one of the rows in the join. You have to remember that even if you want there to be duplicates, they will be eliminated even if they are suppose to be there.
The full alter would be:
ALTER PROCEDURE [dbo].[mc_Workouts_GetActivities]
#WorkoutID bigint
AS
BEGIN
SET NOCOUNT ON
SELECT d.ID, a.Description,
CASE WHEN Reps = 0 THEN NULL ELSE Reps END AS Reps,
CASE WHEN Sets = 0 THEN NULL ELSE Sets END AS Sets,
CASE WHEN Minutes = 0 THEN NULL ELSE Minutes END AS Minutes,
d.Comments, c.Name AS Category, a.CategoryID,
(CASE WHEN v.ActivityID IS NULL THEN 0 ELSE 1 END) AS HasVideo,
a.ID AS ActivityID
FROM mc_WorkoutDetails d
inner join (select Reps, Sets, Comments, Minutes, max(id) id
from mc_WorkoutDetails
group by Reps, Sets, Comments, Minutes ) q on q.id = d.id
INNER JOIN mc_Activities a ON d.ActivityID = a.ID
INNER JOIN mc_Activities_Categories c ON a.CategoryID = c.ID
LEFT OUTER JOIN mc_TrainerVideos v ON a.ID = v.ActivityID
WHERE (d.WorkoutID = #WorkoutID)
ORDER BY SortOrder, a.Description
RETURN ##ERROR
END
Thanks for everyone's input. The general suggestions here were correct: I had two rows in the mc_workoutDetails table that referenced the same row in the mc_Activities table.
While the foreign key was part of a unique primary key, it was a compound key and so this column could contain duplicates as long as the other column in the key were different.