Use parameter as column in where in SQL Server stored procedure - c#

BEGIN
DECLARE #SQLQuery AS NVARCHAR(MAX)
IF(#Search IS NOT NULL)
BEGIN
DECLARE #dyColumn sysname ;
IF(#Filter = 'IsNew')
BEGIN
SET #dyColumn = 'IsNew'
END
ELSE IF(#Filter = 'IsOnSale')
BEGIN
SET #dyColumn = 'IsOnSale'
END
ELSE IF(#Filter = 'IsFeatured')
BEGIN
SET #dyColumn = 'IsFeatured'
END
SET #SQLQuery = 'SELECT P.*, C.Id AS CategoryId, C.Name AS CategoryName, C.Logo AS CategoryLogo,
CO.Id AS CompanyId, CO.Name AS CompanyName, CO.Logo AS CompanyLogo, COUNT(*) OVER() TotalCount
FROM Products P
JOIN Categories C ON P.CategoryId = C.Id
JOIN Companies CO ON P.CompanyId = CO.Id
WHERE P.Name LIKE %'+#Search+'% AND '+#dyColumn+' = true
ORDER BY P.Name
OFFSET '+CAST(#PageSize AS nvarchar(100))+'*('+CAST(#PageNumber AS nvarchar(100)) +'- 1) ROWS
FETCH NEXT '+CAST(#PageSize AS nvarchar(100))+'ROWS ONLY OPTION (RECOMPILE);'
EXECUTE(#SQLQuery)
END
This is the query and its giving this error during run time
Incorrect syntax near '2'.
Invalid usage of the option NEXT in the FETCH statement.
which means query getting wrong after
WHERE P.Name LIKE %'+#Search+'%

you don't need dynamic sql for this just test for the value of the filter value in combination with the column in your WHERE expression:
SELECT
P.*
,C.Id AS CategoryId
,C.Name AS CategoryName
,C.Logo AS CategoryLogo
,CO.Id AS CompanyId
,CO.Name AS CompanyName
,CO.Logo AS CompanyLogo
,COUNT(*) OVER() TotalCount
FROM
Products P
JOIN Categories C
ON P.CategoryId = C.Id
JOIN Companies CO
ON P.CompanyId = CO.Id
WHERE
#Search IS NOT NULL
AND P.Name LIKE '%' + #Search + '%'
AND (
(#Filter= 'IsNew' AND IsNew = 1)
OR (#Filter= 'IsOnSale' AND IsOnSale = 1)
OR (#Filter= 'IsFeatured' AND IsFeatured = 1)
OR (#Filter NOT IN ('IsNew','IsOnSale','IsFeatured'))
)
ORDER BY
P.Name
OFFSET (#PageSize)*(#PageNumber)- 1 ROWS
FETCH NEXT (#PageSize) ROWS ONLY OPTION (RECOMPILE);
If you really want to use dynamic SQL instead of Executing it SELECT #SQLQuery and then look for the syntax issues by copying to another query window.

Related

QueryOver with dynamic join and disjuction

I need to create a query that, depending on the input of the method, uses a join or not. Given the following model:
Account
{
Group Group;
Account KeyUserInteral;
Account KeyUserExternal;
DateTime DeactivationDate;
}
Group
{
Account KeyUserInteral;
Account KeyUserExternal;
}
I want to either
get all Accounts that a given different Account is entered as either KeyUser
or
get all Accounts that a given different Account is entered as either KeyUser or is entered as the Account.Groups either KeyUser
and then depending on the DeactivationDate filter that result to include only active Accounts or not.
For this, I tried it using this method:
public IList<Account> ListByKeyUser(int keyUserId_, bool includeGroupAccounts_, bool onlyActive_)
{
Account keyUser = Get(keyUserId_);
Disjunction keyUserRestriction = new Disjunction();
keyUserRestriction.Add<Account>(acc_ => acc_.KeyUserInternal == keyUser || acc_.KeyUserExternal == keyUser);
IQueryOver<Account, Account> query = Session.QueryOver<Account>();
if (includeGroupAccounts_) {
query.JoinQueryOver(acc_ => acc_.Group, JoinType.LeftOuterJoin)
.Where(grp_ => grp_.KeyUserInternal == keyUser || grp_.KeyUserExternal == keyUser);
}
query.Where(keyUserRestriction);
if (onlyActive_) {
query.Where(acc_ => acc_.DeactivationDate > DateTime.Now);
}
return query.OrderBy(acc_ => acc_.Name).Asc.List<Account>();
}
Unfortunatley, the created SQL is not exactly what I need: (Excluded the SELECT as that's not really interesting I think)
SELECT [...]
FROM accounts this_ left outer join groups group1_ on this_.userGroup=group1_.id
WHERE
(group1_.keyUserInternal = #p0 or group1_.keyUserExternal = #p1)
and
((this_.keyUserInternal = #p2 or this_.keyUserExternal = #p3))
and this_.deactivationDate > #p4
ORDER BY this_.name asc;
What I need is this:
SELECT [...]
FROM accounts this_ left outer join groups group1_ on this_.userGroup=group1_.id
WHERE
((group1_.keyUserInternal = #p0 or group1_.keyUserExternal = #p1)
or (this_.keyUserInternal = #p2 or this_.keyUserExternal = #p3))
and this_.deactivationDate > #p4
ORDER BY this_.name asc;
Basically, I just need to somehow move the "join condition" into the "or". I tried it by adding the where into the disjunction:
if (includeGroupAccounts_) {
query.JoinQueryOver(acc_ => acc_.Group, JoinType.LeftOuterJoin);
keyUserRestriction.Add<Account>(acc_ => acc_.Group.KeyUserInternal == keyUser || acc_.Group.KeyUserExternal == keyUser);
}
But that creates:
SELECT [...]
FROM accounts this_ left outer join groups group1_ on this_.userGroup=group1_.id
WHERE
((this_.keyUserInternal = #p0 or this_.keyUserExternal = #p1)
or (this_.keyUserInternal = #p2 or this_.keyUserExternal = #p3))
and this_.deactivationDate > #p4
ORDER BY this_.name asc;
Which totally ignores the Group join...
How can I make this work?
A bit more digging returned the following as a solution:
if (includeGroupAccounts_) {
Group groupAlias = null;
query.JoinAlias(acc_ => acc_.Group, () => groupAlias, JoinType.LeftOuterJoin);
keyUserRestriction.Add(() => groupAlias.KeyUserExternal == keyUser || groupAlias.KeyUserInternal == keyUser);
}
This results in exactly the SQL I needed, where the "join where" is contained in the "or", while the "datetime check" is still an "and" for everything.
SELECT [...]
FROM accounts this_ left outer join groups groupalias1_ on this_.userGroup=groupalias1_.id
WHERE (
(this_.keyUserInternal = #p0 or this_.keyUserExternal = #p1)
or (groupalias1_.keyUserExternal = #p2 or groupalias1_.keyUserInternal = #p3)
)
and this_.deactivationDate > #p4
ORDER BY this_.name asc

joining to tables in a query for ASP.NET C# ~ multi-part-identifier-could-not-be-bound

Among other answers I have looked at the following:
The multi-part identifier could not be bound
However tying to use that answer I was still not able to make my grouping work.
The following query works:
string query = #"SELECT
ud.FirstName,
ud.LastName,
p.Name AS Product,
p.ItemNumber,
sc.AmountPurchased * sc.Price AS Total,
sc.DatePurchased,
sc.IsInCart AS [NotShipped]
FROM
ShoppingCarts sc INNER JOIN
UserDetails ud ON sc.ClientID = ud.Guid Left
OUTER JOIN
Products p ON sc.ProductID = p.ProductsId
WHERE sc.DatePurchased >= #date1
AND sc.DatePurchased <= #date2
AND sc.IsInCart = #shipped ";
but when I try to group it like this:
string query = #"SELECT ud.LastName, sc.DatePurchased, SUM(Total)
FROM(
SELECT
ud.FirstName,
ud.LastName,
p.Name AS Product,
p.ItemNumber,
sc.AmountPurchased * sc.Price AS Total,
sc.DatePurchased,
sc.IsInCart AS [NotShipped]
FROM
ShoppingCarts sc INNER JOIN
UserDetails ud ON sc.ClientID = ud.Guid Left
OUTER JOIN
Products p ON sc.ProductID = p.ProductsId
WHERE sc.DatePurchased >= #date1
AND sc.DatePurchased <= #date2
AND sc.IsInCart = #shipped
) AS result
GROUP BY ud.LastName, sc.DatePurchased";
I get the "multi-part identifier ud.LastName, etc cannot be bound"
When doing nested selects the aliases do not propagate out of their scope. This is more evident when looking at the equivalent CTE query (assuming you're using MS SQL Server / T-SQL):
;WITH
(
SELECT
ud.FirstName,
ud.LastName,
p.Name AS Product,
p.ItemNumber,
sc.AmountPurchased * sc.Price AS Total,
sc.DatePurchased,
sc.IsInCart AS [NotShipped]
FROM
ShoppingCarts sc INNER JOIN
UserDetails ud ON sc.ClientID = ud.Guid Left
OUTER JOIN
Products p ON sc.ProductID = p.ProductsId
WHERE sc.DatePurchased >= #date1
AND sc.DatePurchased <= #date2
AND sc.IsInCart = #shipped
) AS Result
SELECT
LastName,
DatePurchased,
SUM(Total)
FROM Result
GROUP BY
LastName,
DatePurchased
So your problem is solved by removing the ud and sc identifiers from the outer select and group by.

How can I retrieve data from joined tables?

I have 3 tables:
products table
--------------
id
name
...
categories table
----------------
id
name
...
product_categories table
------------------------
product_id
category_id
And join them by this query:
select p.*
from products p
join product_categories pc on pc.product_id = p.id
join categories c on pc.category_id = c.id
This query return multiple records of a product per each category of that product, but I want to get only one product and multiple category of that product.
This is my output:
p.Id p.name --> cat_id cat_name
1 product_1 --> 1 cat1
1 product_1 --> 3 cat3
1 product_1 --> 2 cat2
1 product_1 --> 6 cat6
2 product_2 --> 5 cat5
2 product_2 --> 1 cat1
.
.
.
And desired output:
p.Id p.name --> cat_id cat_name,cat_id cat_name,...
1 product_1 --> 1 cat1,3 cat3,2 cat2,6 cat6
2 product_2 --> 5 cat5,1 cat1
.
.
.
How can I do that?
You should add WHERE clause to specify ID and in select clause maybe you want to use c.* instead of p.* to get all data from categories.
select p.name, c.*
from products p
join product_categories pc on pc.product_id = p.id
join categories c on pc.category_id = c.id
where p.id = --smth
If you don't want the redundancy of the product records, in other words you only want the category information and nothing else, then you don't need the join to the main products table in the first place (assuming you're using the product id).
However, you're problem is that unless you're planning to display a column for every possible category and whether the product has it or not then you're going to need to do a work around. My preferred approach would be to create a cursor that runs through each row and pulls the categories for a given product id into a single string and puts this string into a new table (product id, categories).
DECLARE #productId int
DECLARE #prevProductId int
SET #prevProductId = 0
DECLARE #cid int
DECLARE #cname nvarchar(255)
DECLARE #categoryList nvarchar(max)
SET #categoryList = ''
DECLARE #ProdCatTable TABLE
(
ProductId int,
Categories nvarchar(max)
)
DECLARE category_cursor CURSOR FOR
SELECT pc.product_id, c.id, c.name
FROM categories c
JOIN product_categories pc
ON pc.category_id = c.id
ORDER BY pc.product_id
OPEN category_cursor
FETCH NEXT FROM category_cursor
INTO #productId, #cid, #cname
WHILE ##FETCH_STATUS = 0
BEGIN
IF #prevProductId = 0
BEGIN
SET #categoryList = #cid + ' ' + #cname
SET #prevProductId = #productId
END
ELSE IF (#prevProductId <> #productId) AND #categoryList <> ''
INSERT INTO #ProdCatTable (ProductId, Categories)
VALUES (#prevProductId, #categoryList)
SET #prevProductId = #ProductId
SET #categoryList = ''
ELSE
BEGIN
SET #categoryList = #categoryList + ', ' + #cid + ' ' + #cname
END
FETCH NEXT FROM category_cursor
INTO #productId, #cid, #cname
END
CLOSE category_cursor
DEALLOCATE category_cursor
SELECT ProductId, Catgories
FROM #ProdCatTable
NOTE: This hasn't been tested, but I think it should work if you're happy to return a table with the product id as your identify and the categories as a string.
SELECT Tbl_Emp.EMPId, Tbl_Customer.First_Name, Tbl_Mail_IDs.MailId
FROM Tbl_Emp INNER JOIN
Tbl_Mail_IDs ON Tbl_Emp.EMPId = Tbl_Mail_IDs.Id INNER
JOIN
Tbl_Customer ON Tbl_Mail_IDs.Id = Tbl_Customer.Customer_Id

How can we assign local variable in sub query SQL Server

I tried to set a value to variable in sub query but it doesn't work.
Here is my query:
declare #val1 int
declare #val2 int
select #val1 = sum(column1)
,(select #val2 = (select sum(column2) from table2))
,(#val1+#val2)Result
from table 1
What I want to do is setting #val2 for sub query help me please
I meant set in Sub query not separate select statement
Just use 3 separate selects:
select #val1 = sum(column1) from table1
select #val2 = sum(column2) from table2
select (#val1+#val2) as Result
Or you can also write 2 selects:
select #val1 = sum(column1),
#val2 = (select SUM(column2) from table2)
from table1
select (#val1 + #val2) Result
But not just 1 select:
A SELECT statement that assigns a value to a variable must not be
combined with data-retrieval operations
If you need to accomplish all in one select and return a recordset, do not use variables, do it like this:
SELECT sum1 + sum2 FROM (
select sum(column1) as sum1,
(select SUM(column2) from table2) as sum2
from table1
) subquery

Dynamic SQL Server Query to Fill DataTable

Hello I need to do the follow query in SQL Server to fill and Datatable.
In my case I have 3 tables:
1 - Users
2 - Process
3 - Status
Users
id, name
Process
id, cod_user, cod_status
Status
id, status
I need to make an query that return the follow table:
User.Name | Status.Created | Status.Opened | Status.Finalized
Tom 50 30 20
Roger 22 33 44
Kris 11 09 05
And then, I have to return this table to fill and datatable.
Thanks...
Looks like you are trying to PIVOT the data, you can use something like this if you know the values of status to transform:
select name, [Created], [Opened], [Finalized]
from
(
select u.name,
s.status
from users u
left join process p
on u.id = p.cod_user
left join status s
on p.cod_status = s.id
) src
pivot
(
count(status)
for status in ([Created], [Opened], [Finalized])
) piv
If you have an unknown number of statuses to turn into columns, then you can use dynamic sql:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(status)
from status
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT name, ' + #cols + ' from
(
select u.name,
s.status
from users u
left join process p
on u.id = p.cod_user
left join status s
on p.cod_status = s.id
) x
pivot
(
count(status)
for status in (' + #cols + ')
) p '
execute(#query)
If you don't have access to the PIVOT function, then you can replicate it using an aggregate function and a CASE statement:
select u.name,
sum(case when s.status = 'created' then 1 else 0 end) created,
sum(case when s.status = 'opened' then 1 else 0 end) opened,
sum(case when s.status = 'finalized' then 1 else 0 end) finalized
from users u
left join process p
on u.id = p.cod_user
left join status s
on p.cod_status = s.id
group by u.name
Looks like you're trying to show one record per User, with one column per Status, and the columns contain the count of records in the Process table with that status for that user?
If that's correct, then you want to use a Pivot Table.

Categories