I'm having a strange problem with a linq query. I'm using LINQPad 4 to make some a query that uses regular expression using LinqToSQL as the LinqPad driver.
Here's the query that I'm trying to make :
(from match in
from s in SystemErrors
select Regex.Match(s.Description, "...")
select new
{
FamilyCode = match.Groups["FamilyCode"].Value,
ProductPrefix = match.Groups["ProductPrefix"].Value,
BillingGroup = match.Groups["BillingGroup"].Value,
Debtor = match.Groups["Debtor"].Value
}).Distinct()
As you can see I'm trying to extract data from a text description in a log table using groups. The query works, but the Distinct doesn't want to work, it returns a line for all Match.
I have read that distinct should work with anonymous type, matching each property. Even more strange is that distinct does actually do something, it orders the values alphabetically by the FamilyCode (and then by ProductPrefix, etc.).
Has someone an idea on why this isn't working?
Thanks
Here is what is displayed in the SQL tab of LinqPad :
DECLARE #p0 NVarChar(1000) = 'Big Regexp'
DECLARE #p1 NVarChar(1000) = 'FamilyCode'
DECLARE #p2 NVarChar(1000) = 'ProductPrefix'
DECLARE #p3 NVarChar(1000) = 'BillingGroup'
DECLARE #p4 NVarChar(1000) = 'Debtor'
SELECT DISTINCT [t2].[Description] AS [input], [t2].[value], [t2].[value2], [t2].[value3], [t2].[value4], [t2].[value5]
FROM (
SELECT [t1].[Description], [t1].[value], #p1 AS [value2], #p2 AS [value3], #p3 AS [value4], #p4 AS [value5]
FROM (
SELECT [t0].[Description], #p0 AS [value]
FROM [SystemError] AS [t0]
) AS [t1]
) AS [t2]
var result = from eachError in SystemErrors
let match = Regex.Match(eachError.Description, "...")
group eachError by new
{
FamilyCode = match.Groups["FamilyCode"].Value,
ProductPrefix = match.Groups["ProductPrefix"].Value,
BillingGroup = match.Groups["BillingGroup"].Value,
Debtor = match.Groups["Debtor"].Value
}
into unique
select unique.key;
When you use Distinct(), it's distinct by pointer to each object, not value because select new {} is object type not value type. Try using group by instead.
On the other hand, you can use .Distinct(IEqualityComparer<T>) overload and provided EqualityComparer for the object that you want to process.
Related
I have a stored procedure which looks like following:
alter procedure [dbo].[zsp_deleteEndedItems]
(
#ItemIDList nvarchar(max)
)
as
delete from
SearchedUserItems
WHERE EXISTS (SELECT 1 FROM dbo.SplitStringProduction(#ItemIDList,',') S1 WHERE ItemID=S1.val)
The parameter IDList is passed like following:
124125125,125125125...etc etc
And the split string function look like following:
ALTER FUNCTION [dbo].[SplitStringProduction]
(
#string nvarchar(max),
#delimiter nvarchar(5)
) RETURNS #t TABLE
(
val nvarchar(500)
)
AS
BEGIN
declare #xml xml
set #xml = N'<root><r>' + replace(#string,#delimiter,'</r><r>') + '</r></root>'
insert into #t(val)
select
r.value('.','varchar(500)') as item
from #xml.nodes('//root/r') as records(r)
RETURN
END
This is supposed to delete all items from table "SearcheduserItems" under the IDs:
124125125 and 125125125
But for some reason after I do a select to check it out:
select * from SearchedUserItems
where itemid in('124125125','125125125')
The records are still there...
What am I doing wrong here? Can someone help me out?
As mentioned in the comments, a different option would be to use a table type parameter. This makes a couple of assumptions (some commented), however, should get you on the right path:
CREATE TYPE dbo.IDList AS TABLE (ItemID int NOT NULL); --Assumed int datatype;
GO
ALTER PROC dbo.zsp_deleteEndedItems #ItemIDList dbo.IDList READONLY AS
DELETE SUI
FROM dbo.SearchedUserItems SUI
JOIN #ItemIDList IDL ON SUI.ItemID = IDL.ItemID;
GO
--Example of usage
DECLARE #ItemList dbo.IDList;
INSERT INTO #ItemList
VALUES(123456),(123457),(123458);
EXEC dbo.zsp_deleteEndedItems #ItemList;
GO
In regards to the question of an inline table value function, one such example is the below, which I quickly wrote up, that provides a tally table of the next 1000 numbers:
CREATE FUNCTION dbo.NextThousand (#Start int)
RETURNS TABLE
AS RETURN
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL)) N(N)
)
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 + #Start AS I
FROM N N1 --10
CROSS JOIN N N2 --100
CROSS JOIN N N3; --1,000
GO
The important thing about an iTVF is that it has only one statement, and that is the RETURN statement. Declaring the table as a return type variable, inserting data into it, and returning that variable turns it into a multi-line TVF; which perform far slower.
SELECT Col1, Col2, Col3, Col4
FROM Table1
WHERE User1 = #Owner
AND group1 = #Group
AND date1 BETWEEN #startDate AND #endDate
AND Mail LIKE #email
AND def IN (CASE #InvoiceMethod //Problem is Here
WHEN ''
THEN def
ELSE (#InvoiceMethod)
END)
A piece of code from the stored procedure. If am executing this, it's not returning any rows, even though it has some to return. Problem is with the IN clause, if I didn't pass anything to IN clause i.e #InvoiceMethod is null, then I'm getting rows.
If I pass anything to #InvoiceMethod, I'm not getting any rows.
The value in #InvoiceMethod is = 'A','B'
I tried many combinations like 'A','B' or "A","B" without any results.
How to pass values to IN clause please? In which format?
Please help me out of this.
Modified the stored procedure to the following,
Declare #tmpt table (value nvarchar(5) not null)
SET #InvoiceCount=(select COUNT(*) from dbo.fnSplit(#InvoiceMethod, ','))
SET #tempVar=1;
WHILE #tempVar<=(#InvoiceCount)
BEGIN
INSERT INTO #tmpt (value)
VALUES (#InvoiceMethod);//Here i need to insert array of values to temp table.like invoicemethod[0],invoicemethod[1]&invoicemethod[2] depends on #InvoiceCount
SET #tempVar=#tempVar+1;
END
--DECLARE #tmpt TABLE (value NVARCHAR(5) NOT NULL)
--INSERT INTO #tmpt (value) VALUES (#InvoiceMethod);
SELECT Col1,Col2,Col3,Col4
FROM Table1
WHERE User1 = #Owner
AND group1 = #Group
AND date1 between #startDate AND #endDate
AND Mail LIKE #email
AND def IN (SELECT value FROM #tmpt)
But not getting the results as expected :(
IMO this isn't a good way to approach this problem, by passing a list of filter values for a column in a comma separated string, as this is almost encouraging a Dynamic Sql approach to the problem (i.e. where you EXEC a built Sql string which pastes in the #InvoiceMethod as a string).
Instead, Sql 2008 has Table Valued Parameters, (and prior to this, you could use Xml), which allows you to pass structured data into a procedure in a table format.
You then just need to join to this table parameter to effect the 1..N valued IN () filtering.
CREATE TYPE ttInvoiceMethods AS TABLE
(
Method VARCHAR(20)
);
GO
CREATE PROCEDURE dbo.SomeProc
(
#InvoiceMethod ttInvoiceMethods READONLY, -- ... Other Params here
)
AS
begin
SELECT Col1, Col2, ...
FROM Table1
INNER JOIN #InvoiceMethod
ON Table1.def = #InvoiceMethod.Method -- Join here
WHERE User1 = #Owner
... Other Filters here
END
Have a look here for a similar solution with a fiddle.
Edit
The optional parameter (#InvoiceMethod = '') can be handled by changing the JOIN to the TVP with a subquery:
WHERE
-- ... Other filters
AND (Table1.def IN (SELECT Method FROM #InvoiceMethod))
OR #InvoiceMethod IS NULL)
To Initialize a TVP to NULL, just don't bind to it in C# at all.
I think a variable represetning multiple values with comma is not allowed in the in clause. You should either use string fiunctions (split and join) or go with the temp table solution. I prefer the second.
Use a temporary table to store your values and then pass it to your in statement
DECLARE #tmpt TABLE (value NVARCHAR(5) NOT NULL)
INSERT INTO #tmpt .........
...
...
SELECT Col1,Col2,Col3,Col4
FROM Table1
WHERE User1 = #Owner
AND group1 = #Group
AND date1 BETWEEN #startDate AND #endDate
AND Mail LIKE #email
AND def IN (SELECT value FROM #tmpt)
Used Splitfunctions to resolve the issue,Modified SQL Query
SELECT Col1, Col2, Col3, Col4
FROM Table1
WHERE User1 = #Owner
AND group1 = #Group
AND date1 BETWEEN #startDate AND #endDate
AND Mail LIKE #email
AND def IN (SELECT * FROM sptFunction(#InvoiceMethod,',')) //Problem is Here (Solved by using split functions)
This is a bit confusing because it worked before and I added 1 little change and I get this error message in my web application. (I do have other cases, I just took out the other cases for simplicity)
Original code that worked before:
ALTER PROCEDURE [dbo].[GetRoles]
(#reportid Decimal, #dom varchar(10))
AS
DECLARE #sql varchar(2000)
SELECT #sql =
Case #reportid
WHEN 1 THEN
'select u.id as userId, u.domain, u.isAdmin, u.email, u.canReport, a.[site], a.bldgNum, a.dataCenterNum, l.shortName, l.[description], a.canApprove, a.canComplete
from locAdmin a
inner join location l on (a.site=l.site and a.bldgNum = l.bldgNum and a.dataCenterNum = l.dataCenterNum)
right outer join [user] u on u.id=a.userId and u.domain=a.domain
where u.isAdmin = 1'
End
EXEC (#sql)
The only change I did was adding
and u.domain = #dom'
after where u.isAdmin = 1' at the end so it looks like this
where u.isAdmin = 1 and u.domain = #dom'
You need to add #dom as a parameter when you execute your dynamic SQL.
Replace EXEC (#sql) with exec sp_executesql #sql, N'#dom varchar(10)', #dom and change DECLARE #sql varchar(2000) to DECLARE #sql nvarchar(2000).
You don't need dynamic SQL to to this write this query.
Please do it like this:
ALTER PROCEDURE [dbo].[GetRoles]
(
#reportid Decimal,
#dom varchar(10)
)
AS
select u.id as userId, u.domain, u.isAdmin, u.email, u.canReport,
a.[site], a.bldgNum, a.dataCenterNum,
l.shortName, l.[description],
a.canApprove, a.canComplete
from locAdmin a
inner join location l
on a.site=l.site and a.bldgNum = l.bldgNum and a.dataCenterNum = l.dataCenterNum
right outer join [user] u
on u.id=a.userId and u.domain=a.domain
where u.isAdmin = 1 and u.domain = #dom and #reportid = 1
You need to actually add the value of the variable to your dynamic SQL:
'THE REST OF YOUR QUERY
where u.isAdmin = 1 and u.domain = ''' + #dom + ''''
This is because your dynamic SQL is another SQL statement that you are going to execute, and therefore doesn't know about the parameter #dom in your original statement.
I was wondering if the below scenario will work? I am having trouble with it.
I have a smart tag SQLDataSource with a query like such:
SELECT [col1], [col2], [col3] FROM [Table1] WHERE (#SubType = #SubID) ORDER BY [col1] ASC
No matter where or how I set the #SubType parameter, it does not work, yet if I change the query to WHERE [col1] = #SubID (removing the #SubType) it works fine.
Can I set a parameter as a field name to compare against like my query does?
That's not how parameters work. Parameters are not string replacement. They work with values, not database objects names (Columns, Tables, etc.).
The solution is to first assemble the SQL query with the desired columns (code behind) and then set the parameter's values.
If you want to dynamically replace the items in your WHERE clause then you will want to look at using Dynamic SQL, then you can build your SQL as a string and execute it.
Code sample from http://www.sommarskog.se/dynamic_sql.html
DECLARE #sql nvarchar(2000)
SELECT #sql = 'SELECT O.OrderID, SUM(OD.UnitPrice * OD.Quantity)
FROM dbo.Orders O
JOIN dbo.[Order Details] OD ON O.OrderID = OD.OrderID
WHERE O.OrderDate BETWEEN #from AND #to
AND EXISTS (SELECT *
FROM dbo.[Order Details] OD2
WHERE O.OrderID = OD2.OrderID
AND OD2.ProductID = #prodid)
GROUP BY O.OrderID'
EXEC sp_executesql #sql, N'#from datetime, #to datetime, #prodid int',
'19980201', '19980228', 76
Another helpful link:
Dynamic WHERE Clause
The stored procedure:
ALTER PROC [Admin].[sp_Ques]
(
#QuesID bigint
)
AS
BEGIN
IF #QuesID = 0
SET #QuesID =NULL
SELECT FQ.QuesID, FQ.Ques,QuesAns
FROM Admin.Ques FQ
WHERE FQ.QuesID = Coalesce(#QuesID,QuesID)
SELECT Language FROM Admin.Language WHERE LanguageID=FQ.LanguageID
END
In the second Select statement:
SELECT Language FROM Admin.Language WHERE LanguageID=FQ.LanguageID
In this statement, I want the value of "FQ.LanguageID" from 1st select statement, so I wrote this:-
LanguageID=FQ.LanguageID
Apparently didn't work. It says "The multi-part identifier "FQ.LanguageID" could not be bound."
Do I need to pass this LanguageID to the stored procedure as a parameter and then use it as:-
SELECT Language FROM Admin.Language WHERE LanguageID=#LanguageID
How can I make this LanguageID=FQ.LanguageID work if I don't want to pass LanguageID as the second argument to the stored procedure? Is there a way?
Perhaps create a local variable to hold the LanguageID that's being retrieved. Assign a value to it during the previous SELECT. The addition of TOP 1 simply ensures that if/when you ever have multiple matches in the first query (indeed you will when #Ques is zero or null!), only one value is returned in that query, thereby allowing a single value into your variable.
DECLARE #Lang int --whatever datatype your QuesID is.
SELECT TOP 1
FQ.QuesID, FQ.Ques,QuesAns as QuesAns,
FQ.QuesAns[Answers], FQT.QuesType ,
FQ.QuesTypeID, FQ.QuesParentID, FQ.Active, FQ.AdminLanguageID
,#Lang = FQ.AdminLanguageID
FROM Admin.Ques FQ
LEFT OUTER JOIN Admin.QuesTypes FQT ON FQT.QuesTypeID=FQ.QuesTypeID
WHERE FQ.QuesID = Coalesce(#QuesID,QuesID)
SELECT TelerikLanguage FROM Admin.Language
WHERE AdminLanguageID=#Lang
The scope of FQ is limited to the first select statement.
Your options include:
Passing AdminLanguageID as a parameter as you have suggested
Retrieving AdminLanguageID in a prior statement (select #AdminLanguageID = AdminLanguageID from...)
Joining Admin.Language with Admin.Ques
Using a subquery (select ... from Admin.Language where AdminLanguageID in (select AdminLanguageID from Admin.Ques where ...)
Why not just join them into 1 select?
ALTER PROC [Admin].[sp_Ques]
(
#QuesID bigint
)
AS
BEGIN
IF #QuesID = 0
SET #QuesID =NULL
SELECT FQ.QuesID, FQ.Ques,QuesAns as QuesAns,FQ.QuesAns[Answers], FQT.QuesType ,FQ.QuesTypeID, FQ.QuesParentID, FQ.Active,FQ.AdminLanguageID, AL.TelerikLanguage
FROM Admin.Ques FQ
LEFT OUTER JOIN Admin.QuesTypes FQT ON FQT.QuesTypeID=FQ.QuesTypeID
LEFT JOIN Admin.Language AL ON AL.AdminLanguageID=FQ.AdminLanguageID
WHERE FQ.QuesID = QuesID OR #QuesID IS NULL
END