SQL Server + Dynamic Query 'Invalid Column Name' - sql

I am trying to execute the following dynamic query but I got an error: Invalid column name 'cat'
DECLARE #SQLDelQuery AS NVARCHAR(1200)
DECLARE #MemberNames varchar(50)
SET #MemberNames = 'cat'
SET #SQLDelQuery = 'SELECT [Email] FROM [aspnet_Membership] am
INNER JOIN [aspnet_Users] u
ON (am.UserId = u.UserId)
INNER JOIN [Member] m
ON (am.UserId = m.UserId)
WHERE u.UserName IN (' + #MemberNames + ')
EXECUTE(#SQLDelQuery)
If I change it to the normal query I am fine:
SELECT [Email] FROM [aspnet_Membership] am
INNER JOIN [aspnet_Users] u
ON (am.UserId = u.UserId)
INNER JOIN [Member] m
ON (am.UserId = m.UserId)
WHERE u.UserName IN ('cat')
Anyone can point out my error? Thanks.

Since cat is a varchar you need to include single quotes around it and you need to place the closing parentheses for the IN clause inside of the sql string.
The new code will be:
DECLARE #SQLDelQuery AS NVARCHAR(1200)
DECLARE #MemberNames varchar(50)
SET #MemberNames = 'cat'
SET #SQLDelQuery = 'SELECT [Email] FROM [aspnet_Membership] am
INNER JOIN [aspnet_Users] u
ON (am.UserId = u.UserId)
INNER JOIN [Member] m
ON (am.UserId = m.UserId)
WHERE u.UserName IN (''' + #MemberNames + ''')'
EXECUTE(#SQLDelQuery)
See a SQL Fiddle Demo with the query string printed. This generates a query string like this:
SELECT [Email]
FROM [aspnet_Membership] am
INNER JOIN [aspnet_Users] u
ON (am.UserId = u.UserId)
INNER JOIN [Member] m
ON (am.UserId = m.UserId)
WHERE u.UserName IN ('cat') -- cat surrounded in single quotes

You need to pass it as a string to the dynamic query
SET #MemberNames = '''cat'''
Difference in the resulted query is
WHERE u.UserName IN (cat) -- cat is taking as a column name here
WHERE u.UserName IN ('cat') -- cat is taking as a string here

Your string:
WHERE u.UserName IN (' + #MemberNames + ')
will evaluate to:
WHERE u.UserName IN (cat)
because the apostrophes you have are just encapsulating the string, and there are no extra apostrophes around the string literal.
You need:
WHERE u.UserName IN (''' + #MemberNames + ''')
Alternately, you can leave your query as is, and seperate each ID with apostrophes in your #MemberNames variable:
SET #MemberName = '''cat''' -- for a single user
SET #MemberName = '''cat'', ''dog''' -- for multiple users

Your dynamic query uses ' characters as the string delimiter, so the last line ends up reading like this after the string is built:
WHERE u.UserName IN (cat)
According to this, cat reads like a column name.
To fix it, you need to include escaped ' characters in either the definition of
`SET #MemberNames = '''cat'''`
or in the string being used to build the sql:
`WHERE u.UserName IN (''' + #MemberNames + ''')'`

Related

How to output each value from raw via Print or something else in SQL

I have code
DECLARE #UserRolesList NVARCHAR(MAX) = 'Admin, Dog, BranchManager, Naid'
--DECLARE #UserRolesList NVARCHAR(MAX) = 't1, t2, t3'
DECLARE #UserRoles TABLE (Name VARCHAR(max))
INSERT INTO #UserRoles
SELECT value
FROM STRING_SPLIT(#UserRolesList, ',')
SELECT * FROM UserRole u
JOIN #UserRoles r ON u.Name = TRIM(r.Name)
And I need output each value from a row of joining result something like that Print('Admin, ' Adminitration role ...') and so on, how I can do this?
I suspect that you want the roles concatenated together for each user:
SELECT u.user_id, STRING_AGG(u.name, ', ') as roles
FROM UserRole u JOIN
#UserRoles r
ON u.Name = TRIM(r.Name)
GROUP BY u.user_id;

Unable to apply the filters on the Sql with Join

Below is my stored procedure. In which I am getting the details from multiple tables.
ALTER PROCEDURE [dbo].[proc_getCoachList]
#skills varchar(MAX), -------- example // '["Life", "Sports"]'
#keyword varchar(MAX) -------- example // "developer"
AS
BEGIN
(select SkillTitle from ValidSkills)
END
BEGIN
SELECT DISTINCT users.*,
profiles.Details,
profiles.Experiance,
profiles.HoursCompleated,
profiles.RatePerHour,
profiles.SubTitle,
profiles.TotalEarning,
UserSkils.UserSkills
FROM Users users
LEFT JOIN profile profiles ON users.UserID =
profiles.UserID
LEFT JOIN
(
SELECT DISTINCT ts2.UserId,
(
SELECT ts.SkillDescription + ',' AS [text()]
FROM Skills ts
WHERE ts.UserId = ts2.UserId
ORDER BY ts.UserId
FOR XML PATH ('')
) AS UserSkills
FROM Skills ts2
) UserSkils ON users.UserID = UserSkils.UserId
My filter starts from here---------
WHERE EXISTS (
SELECT 1
FROM OPENJSON(#skills, '$') AS j
WHERE UserSkils.UserSkills LIKE '%' + j.value + '%'
) OR
users.UserName LIKE '%' + #keyword + '%'
END
Here My filters are not working properly. Filter is based on multiple skills and keyword.Please let me know the best way and proper filter inside the where clause.....
The special case is when #skills are empty then its not working according keyword also.
The LIKE is a tricky function. The query table field (j.value) should be on the data side of the LIKE instruction (left), not on the match side (right). Maybe you can try using
WHERE CHARINDEX(j.value,UserSkils.UserSkills)

SQL Server Flatten Data

I have 3 tables:
tUsers
-uid
tColors
-colorid
-colorname
tColors_User_Detail
-uid_fk
-colorid_fk
Users select which colors they like, and only the colors they like. This creates records in tColors_User_Detail. I need to flatten this out, so that each user has one record with the color from tColors as a column name, and they have a True/False value in the row for each color depending on if they had a record in tColors_User_Detail. If the user did not have a color selected in tColors_User_Detail, it would be a False value in the specific color column. And, if they do have a record in tColors_User_Detail for a color, it would be a true value for the corresponding color column.
Any help appreciated.
Here's a basic PIVOT example with a COALESCE to show 'false' if no value is available. This assumes you have to hard-code the names of the colors for the column names.
DECLARE #tUsers TABLE ([uid] INT)
DECLARE #tColors TABLE ([colorid] INT, [colorname] VARCHAR(50))
DECLARE #tColors_User_Detail TABLE ([uid_fk] INT, [colorid_fk] INT)
INSERT #tUsers VALUES (1),(2)
INSERT #tColors VALUES (1,'Blue'),(2,'Red'),(3,'Green')
INSERT #tColors_User_Detail VALUES (1,1),(1,2),(1,3),(2,1)
SELECT
uid,
COALESCE([Red], 'False') AS [Red],
COALESCE([Blue], 'False') AS [Blue],
COALESCE([Green], 'False') AS [Green]
FROM #tUsers U
LEFT OUTER JOIN #tColors_User_Detail CUD
ON CUD.uid_fk = U.uid
LEFT OUTER JOIN #tColors C
ON C.colorid = CUD.colorid_fk
PIVOT (MAX(colorname) FOR colorname IN (
[Red],
[Blue],
[Green]
)) PVT
If you want to let the columns be dynamic from the colors, you'll have to use dynamic sql.
DECLARE #Sql VARCHAR(1000) =
'SELECT uid'
+ (SELECT ', CASE WHEN [' + [colorname] + '] IS NOT NULL THEN ''True'' ELSE ''False'' END AS [' + [colorname] + ']' AS [text()] FROM tColors FOR XML PATH(''))
+ ' FROM tUsers U
LEFT OUTER JOIN tColors_User_Detail CUD
ON CUD.uid_fk = U.uid
LEFT OUTER JOIN tColors C
ON C.colorid = CUD.colorid_fk
PIVOT (MAX(colorname) FOR colorname IN ('
+ SUBSTRING((SELECT ',[' + [colorname] + ']' AS [text()] FROM tColors FOR XML PATH('')), 2, 1000)
+ ')) PVT'
EXEC (#Sql)
What flavor of SQL?
Something along the lines of:
http://sqlfiddle.com/#!6/ec4e2
SELECT U.uid
, C.colorid
, C.colorname
, ( CASE WHEN cud.uid_fk IS NOT NULL THEN 'True' ELSE 'False' END ) AS ColorChosen
FROM tUsers U
FULL OUTER JOIN tColors C ON 1=1
LEFT OUTER JOIN tColors_User_Detail cud ON
U.uid = cud.uid_fk
AND C.colorid = cud.colorID_FK
EDIT: I missed the pivot for one row per user. Meeting time though. Be back in a bit.

Use Variable value in a SELECT statement

i have declared a variable #S and i am storing a sub query value in it which is returning more than one record , now what i want is to use it in a select statement , can i do that is it possible,
here is my query what i was trying but getting an error Must declare the scalar variable "#S"
Declare #S AS NVarchar(MAX)
SET #S = '(SELECT es.FirstName FROM [User] es WHERE es.UserId IN (SELECT CustomerUserID FROM OrderInfo))'
SELECT
OrderInfoId,
BorrowerFirstName As ConsumerFirstName,
BorrowerLastName As ConsumerLastName,
RequestedURL,
Requests,
SELECT #S,
u.FirstName +'' ''+ u.LastName As Affiliate,
o.RequestDateTime As DateOfTransaction,
o.RequestIPAddress As OriginatingIPAddress,
o.Requests As Status
from orderInfo o
inner join [User] u on o.AffiliateId = u.UserId
is it possible to do that. any help will be appreciated
If you want to append as a single string the result of the sub-query to every row produced by the main query, then, first of all you have to initialize #S:
Declare #S AS NVarchar(MAX) = ''
then properly set it:
SELECT #S = #S + ' ' + es.FirstName
FROM [User] es
WHERE es.UserId IN (SELECT CustomerUserID FROM OrderInfo)
and finally consume its contents in the main query:
SELECT
OrderInfoId,
BorrowerFirstName As ConsumerFirstName,
BorrowerLastName As ConsumerLastName,
RequestedURL,
Requests,
(SELECT #S),
u.FirstName +'' ''+ u.LastName As Affiliate,
o.RequestDateTime As DateOfTransaction,
o.RequestIPAddress As OriginatingIPAddress,
o.Requests As Status
from orderInfo o
inner join [User] u on o.AffiliateId = u.UserId
Your biggest issue is that you're trying to select multiple rows into a single column. This can't be done. You will need to concatenate the FirstNames into a single value. There are a few ways to do this. One popular way is to use STUFF with FOR XML This is an example of how you would get all of the FirstNames that match your sub query into a column. The names will be comma separated.
SELECT
OrderInfoId,
BorrowerFirstName AS ConsumerFirstName,
BorrowerLastName AS ConsumerLastName,
RequestedURL,
Requests,
STUFF((SELECT
', ' + es.FirstName
FROM
[User] es
WHERE
es.UserId IN (SELECT
CustomerUserID
FROM
OrderInfo)
ORDER BY
es.FirstName
FOR
XML PATH('')
),1,2,'') AS CustomerFirstNames,
u.FirstName + ' ' + u.LastName AS Affiliate,
o.RequestDateTime AS DateOfTransaction,
o.RequestIPAddress AS OriginatingIPAddress,
o.Requests AS Status
FROM
orderInfo o
INNER JOIN [User] u ON o.AffiliateId = u.UserId
This seems a little strange too considering you're going to get ALL user first names that have a CustomerID in orderInfo. You might want to filter your sub query by orderInfo.Id or something.
Are you sure you're not just trying to get the CustomerID first name? You can join to [User] again to get this information.
SELECT
OrderInfoId,
BorrowerFirstName AS ConsumerFirstName,
BorrowerLastName AS ConsumerLastName,
RequestedURL,
Requests,
cu.FirstName AS Customer,
u.FirstName + ' ' + u.LastName AS Affiliate,
o.RequestDateTime AS DateOfTransaction,
o.RequestIPAddress AS OriginatingIPAddress,
o.Requests AS Status
FROM
orderInfo o
INNER JOIN [User] u ON o.AffiliateId = u.UserId
INNER JOIN [User] cu ON o.CustomerUserID = cu.UserId
you need to make the entire sql statement a string, and use sp_executesql like so:
DECLARE #OrderID int;
SET #ParmDefinition = N'#SubQuery varchar(255), '+
'#OrderInfoOUT varchar(30) OUTPUT';
SET #S = '(SELECT es.FirstName FROM [User] es WHERE es.UserId IN (SELECT CustomerUserID FROM #OrderInfoOUT = OrderInfo))';
SET #SQL =' OrderInfoId, '+
'BorrowerFirstName As ConsumerFirstName, '+
'BorrowerLastName As ConsumerLastName, '+
'RequestedURL, '+
'Requests, '+
#SubQuery+', '+
'u.FirstName +''' '''+ u.LastName As Affiliate, '+
'o.RequestDateTime As DateOfTransaction, '+
'o.RequestIPAddress As OriginatingIPAddress, '+
'o.Requests As Status'+
'from orderInfo o '+
'inner join [User] u on o.AffiliateId = u.UserId';
sp_executesql #SQL, #ParmDefinition, #SubQuery=#S, #OrderIDOUT=OrderID OUTPUT;
Then you can store the results in variables as I've show with OrderInfoOUT
You won't be able to run it this way. You would need something like sp_executesql
I believe there is a mix of concepts in your question: dynamic SQL with table variables.
If you're using SQL Server 2008 or uper, you have a choice to create table variables that keep alives for the period the hosting routine is running.
You can define something similar to this:
DECLARE #UserInfo TABLE
(
FirstName varchar(50) NOT NULL
);
Load the information as follows:
INSERT #UserInfo
SELECT es.FirstName
FROM [User] es
WHERE es.UserId IN (SELECT CustomerUserID FROM OrderInfo)
Finally you can join table variables similar as temporal tables based on your needs.

Mysql query : If value(s) exist(s) in column, select just the value(s), else select all

Having a Mysql database, I have to select all ( TUser.Name, TAccount.Name ) pairs if TUser.name not exists in variable #UserNames.
If exists, I have to select only the ( TUser.Name, TAccount.Name ) pairs where TUser.name in #UserNames.
Something like the last line of the below query :
DECLARE #UserNames = "Alpha, Beta, Gama";
SELECT User.Name
, Account.Name
FROM TUser
, TAccount
, TUserAccount
WHERE TAccount.ID = TUserAccount.AccountID
AND TUserAccount.UserID = User.ID
-- please rewrite this line
AND TUser.Name IN ( IFNULL ( ( SELECT ID FROM TUser WHERE Name IN #UserNames ) , ( SELECT ID FROM TUser ) ) )
Thank you in advance !
You can't return mutually exclusive result sets with your criteria, without using a IF statement:
SELECT #sum := SUM(FIND_IN_SET(u.name, #UserNames))
FROM TUSER u
IF #sum > 0 THEN
SELECT u.name,
a.name
FROM TUSER u
JOIN TUSERACCOUNT ua ON ua.userid = u.id
JOIN TACCOUNT a ON a.id = ua.accountid
WHERE FIND_IN_SET(u.name, #UserNames) > 0
ELSE
SELECT u.name,
a.name
FROM TUSER u
JOIN TUSERACCOUNT ua ON ua.userid = u.id
JOIN TACCOUNT a ON a.id = ua.accountid
END IF;
You could make that work as a PreparedStatement, MySQL's dynamic SQL, but you still need to run a query to know if you need to return all or a subset.
References:
FIND_IN_SET
IF statements
SELECT
User.Name,
Account.Name
FROM TUser
JOIN TAccount
ON TAccount.Name = TUser.Name
JOIN TUserAccount
ON TUserAccount.AccountID = TAccount.ID
AND TUserAccount.UserID = TUser.ID
LEFT JOIN (
SELECT ID FROM TUser WHERE Name IN #UserNames
) AS UsersFound
ON TUser.ID = UsersFound.ID
Something like that? I haven't tested it though.
Ok, so this is what I would call 'hacks' rather than normall database querying, and I would disrecommend getting in the position you have to deal with in the first place. The right way to deal with a list of items like this is to have your application language parse it into a nice list you can use to build a normal, regular SQL IN list, like so:
TUser.Name IN ('Name1', 'Name2',...., 'NameX')
But anyway, if you want to stick to the original problem and your database is mysql, you can do it in a remotely sane way in a single query like this:
SET #UserNames := 'Alpha,Beta,Gama';
SELECT TUser.Name
, TAccount.Name
FROM TUser
INNER JOIN TUserAccount
ON TUser.ID = TUserAccount.UserID
INNER JOIN TAccount
ON TUserAccount.AccountID = TAccount.ID
WHERE FIND_IN_SET(TUser.Name, #UserNames)
OR (SELECT COUNT(*)
FROM TUser.Name
WHERE FIND_IN_SET(TUser.Name, #UserNames)) = 0
Note that I did change the input data somewhat - I don't have any spaces behind the commas. If that is unacceptable, simply change each occurrence of #UserNames in the query with REPLACE(#UserNames, ', ', ','). Also please note that it's performance down the drain as it is impossible to use any indexes on TUser.Name to filter for specific users.
I already mentioned that you really should make a proper IN list of your data. And you can do so directly in SQL too (dynamic SQL):
SET #UserNames := 'Alpha, Beta, Gama';
SET #stmt := CONCAT(
' SELECT TUser.Name'
,' , TAccount.Name'
,' FROM TUser'
,' INNER JOIN TUserAccount'
,' ON TUser.ID = TUserAccount.UserID'
,' INNER JOIN TAccount'
,' ON TUserAccount.AccountID = TAccount.ID'
,' WHERE TUser.Name IN (''', REPLACE(#UserNames, ', ', ''',''') , ''')'
,' OR (SELECT COUNT(*) '
,' FROM TUser.Name'
,' WHERE TUser.Name IN (''', REPLACE(#UserNames, ', ', ''',''') , ''')) = 0'
)
PREPARE stmt FROM #stmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
(Note that in this case, I could use the user name list with spaces unaltered)