SP: handling nulls - sql-server

I have this Table structure:
Id int not null --PK
Title varchar(50)
ParentId int null --FK to same Table.Id
I'm writing a SP that returns a row's "brothers", here's the code
select * from Table
where Table.ParentId = (select Table.ParentId from Table where Table.id = #Id)
and Table.Id <> #Id
It works perfectly for rows having a parent, but for those who's parent are null (root records), it returns no row. This is working as expected since null = null is always false.
I'm looking for help on how to better design my SP to handle this specific case. I'm not a DBA and my TSQL knowledge is basic.
EDIT: I've updated my SQL query like this:
DECLARE #Id INT
SET #Id = 1
DECLARE #ParentId INT
SET #ParentId = (SELECT Table.ParentId FROM Table WHERE Table.Id = #Id)
SELECT * FROM Table
WHERE (
(#ParentId IS NULL AND (Table.ParentId IS NULL))
OR (Table.ParentId = #ParentId)
)
AND Table.Id <> #Id
It does do the job but if the Id is not in the table, it still returns the row who have no parents. Going to lunch, continue looking at this later.
Thanks in advance,
Fabian

I'm not sure this is the best solution, but you could try to use the COALESCE operator using a "not valid" id for NULL
select * from Table
where COALESCE(Table.ParentId,-1) = (select COALESCE(Table.ParentId,-1) from Table where Table.id = #Id)
and Table.Id <> #Id
Assuming -1 is never used as an ID

It's possible I have not understood your problem description however, in order to return Brothers only when they exist for a given Parent, the following query should suffice:
select Brother.*
from Table Parent
inner join Table Brother on
Parent.id = Brother.ParentID
where Parent.Id= #Id and Brother.Id <> #Id

Related

How to fiind out the missing records (ID) from an indexed [order] table in sql

I have a table [Order] that has records with sequential ID (in odd number only, i.e. 1,3,5,7...989, 991, 993, 995, 997, 999), it is seen that a few records were accidentally deleted and should be inserted back, first thing is to find out what records are missing in the current table, there are hundreds of records in this table
Don't know how to write the query, can anyone kindly help, please?
I am thinking if I have to write a stored procedure or function but would be better if I can avoid them for environment reasons.
Below peuso code is what I am thinking:
set #MaxValue = Max(numberfield)
set #TestValue = 1
open cursor on recordset ordered by numberfield
foreach numberfield
while (numberfield != #testvalue) and (#testvalue < #MaxValue) then
Insert #testvalue into #temp table
set #testvalue = #textvalue + 2
Next
Next
UPDATE:
Expected result:
Order ID = 7 should be picked up as the only missing record.
Update 2:
If I use
WHERE
o.id IS NULL;
It returns nothing:
Since I didn't get a response from you, in the comments, I've altered the script for you to fill in accordingly:
declare #id int
declare #maxid int
set #id = 1
select #maxid = max([Your ID Column Name]) from [Your Table Name]
declare #IDseq table (id int)
while #id < #maxid --whatever you max is
begin
insert into #IDseq values(#id)
set #id = #id + 1
end
select
s.id
from #IDseq s
left join [Your Table Name] t on s.id = t.[Your ID Column Name]
where t.[Your ID Column Name] is null
Where you see [Your ID Column Name], replace everything with your column name and the same goes for [Your Table Name].
I'm sure this will give you the results you seek.
We can try joining to a number table, which contains all the odd numbers which you might expect to appear in your own table.
DECLARE #start int = 1
DECLARE #end int = 1000
WITH cte AS (
SELECT #start num
UNION ALL
SELECT num + 2 FROM cte WHERE num < #end
)
SELECT num
FROM cte t
LEFT JOIN [Order] o
ON t.num = o.numberfield
WHERE
o.numberfield IS NULL;

How do I loop through a table, search with that data, and then return search criteria and result to new table?

I have a set of records that need to be validated (searched) in a SQL table. I will call these ValData and SearchTable respectively. A colleague created a SQL query in which a record from the ValData can be copied and pasted in to a string variable, and then it is searched in the SearchTable. The best result from the SearchTable is returned. This works very well.
I want to automate this process. I loaded the ValData to SQL in a table like so:
RowID INT, FirstName, LastName, DOB, Date1, Date2, TextDescription.
I want to loop through this set of data, by RowID, and then create a result table that is the ValData joined with the best match from the SearchTable. Again, I already have a query that does that portion. I just need the loop portion, and my SQL skills are virtually non-existent.
Suedo code would be:
DECLARE #SearchID INT = 1
DECLARE #MaxSearchID INT = 15000
DECLARE #FName VARCHAR(50) = ''
DECLARE #FName VARCHAR(50) = ''
etc...
WHILE #SearchID <= #MaxSearchID
BEGIN
SET #FNAME = (SELECT [Fname] FROM ValData WHERE [RowID] = #SearchID)
SET #LNAME = (SELECT [Lname] FROM ValData WHERE [RowID] = #SearchID)
etc...
Do colleague's query, and then insert(?) search criteria joined with the result from the SearchTable in to a temporary result table.
END
SELECT * FROM FinalResultTable;
My biggest lack of knowledge comes in how do I create a temporary result table that is ValData's fields + SearchTable's fields, and during the loop iterations how do I add one row at a time to this temporary result table that includes the ValData joined with the result from the SearchTable?
If it helps, I'm using/wanting to join all fields from ValData and all fields from SearchTable.
Wouldn't this be far easier with a query like this..?
SELECT FNAME,
LNAME
FROM ValData
WHERE (FName = #Fname
OR LName = #Lname)
AND RowID <= #MaxSearchID
ORDER BY RowID ASC;
There is literally no reason to use a WHILE other than to destroy performance of the query.
With a bit more trial and error, I was able to answer what I was looking for (which, at its core, was creating a temp table and then inserting rows in to it).
CREATE TABLE #RESULTTABLE(
[feedname] VARCHAR(100),
...
[SCORE] INT,
[Max Score] INT,
[% Score] FLOAT(4),
[RowID] SMALLINT
)
SET #SearchID = 1
SET #MaxSearchID = (SELECT MAX([RowID]) FROM ValidationData
WHILE #SearchID <= #MaxSearchID
BEGIN
SET #FNAME = (SELECT [Fname] FROM ValidationData WHERE [RowID] = #SearchID)
...
--BEST MATCH QUERY HERE
--Select the "top" best match (order not guaranteed) in to the RESULTTABLE.
INSERT INTO #RESULTTABLE
SELECT TOP 1 *, #SearchID AS RowID
--INTO #RESULTTABLE
FROM #TABLE3
WHERE [% Score] IN (SELECT MAX([% Score]) FROM #TABLE3)
--Drop temp tables that were created/used during best match query.
DROP TABLE #TABLE1
DROP TABLE #TABLE2
DROP TABLE #TABLE3
SET #SearchID = #SearchID + 1
END;
--Join the data that was validated (searched) to the results that were found.
SELECT *
FROM ValidationData vd
LEFT JOIN #RESULTTABLE rt ON rt.[RowID] = vd.[RowID]
ORDER BY vd.[RowID]
DROP TABLE #RESULTTABLE
I know this could be approved by doing a join, probably with the "BEST MATCH QUERY" as an inner query. I am just not that skilled yet. This takes a manual process which took hours upon hours and shortens it to just an hour or so.

SQL Server - alternative to using NOT EXISTS

I have a list of about 200,000 records with EntityID column which I load into a temp table variable.
I want to insert any records from the Temp table variable if EntityID from the Temp table does not exist in the dbo.EntityRows table. The dbo.EntityRows table contains about 800,000 records.
The process is very slow compared to when the dbo.EntityRows table had about 500,000 records.
My first guess is because of the NOT EXISTS clause, each row from the Temp variable must scan the entire 800k rows of the dbo.EntityRows table to determine if it exists or not.
QUESTION: Are there alternative ways to run this comparison check without using the NOT EXISTS, which incurs a hefty cost and will only get worse as dbo.EntityRows continues to grow?
EDIT: Appreciate the comments. Here is the query (I left out the part after the IF NOT EXISTS check. After that, if NOT EXISTS, I insert into 4 tables).
declare #EntityCount int, #Counter int, #ExistsCounter int, #AddedCounter int
declare #LogID int
declare #YdataInsertedEntityID int, #YdataSearchParametersID int
declare #CurrentEntityID int
declare #CurrentName nvarchar(80)
declare #CurrentSearchParametersID int, #CurrentSearchParametersIDAlreadyDone int
declare #Entities table
(
Id int identity,
EntityID int,
NameID nvarchar(80),
SearchParametersID int
)
insert into #Entities
select EntityID, NameID, SearchParametersID from YdataArvixe.dbo.Entity order by entityid;
set #EntityCount = (select count(*) from #Entities);
set #Counter = 1;
set #LogID = null;
set #ExistsCounter = 0;
set #AddedCounter = 0;
set #CurrentSearchParametersIDAlreadyDone = -1;
While (#EntityCount >= #Counter)
begin
set #CurrentEntityID = (select EntityID from #Entities
where id = #Counter)
set #CurrentName = (select nameid from #Entities
where id = #Counter);
set #CurrentSearchParametersID = (select SearchParametersID from #Entities
where id = #Counter)
if not exists (select 1 from ydata.dbo.entity
where NameID = #CurrentName)
begin
-- I insert into 4 tables IF NOT EXISTS = true
end
I am not sure but there are following ways how we can check
(SELECT COUNT(er.EntityID) FROM dbo.EntityRows er WHERE er.EntityID = EntityID) <> 0
(SELECT er.EntityID FROM dbo.EntityRows er WHERE er.EntityID = EntityID) IS NOT NULL
EntityID NOT EXISTS (SELECT er.EntityID FROM dbo.EntityRows er)
EntityID NOT IN (SELECT er.EntityID FROM dbo.EntityRows er)
But as per my belief getting count will give good performance.
Also index will help to improve performance as 'Felix Pamittan' said
As #gotqn said, start by using a temporary table. Create an index on EntityID after the table is filled. If you don't have an index on EntityID in EntityRows, create one.
I do things like this a lot, and I generally use the following pattern:
INSERT INTO EntityRows (
EntityId, ...
)
SELECT T.EntityId, ...
FROM #tempTable T
LEFT JOIN EntityRows E
ON T.EntityID = E.EntityID
WHERE E.EntityID IS NULL
Please comment if you'd like further info.
Well, the answer was pretty basic. #Felix and #TT had the right suggestion. Thanks!
I put a non-clustered index on the NameID field in ydata.dbo.entity.
if not exists (select 1 from ydata.dbo.entity
where NameID = #CurrentName)
So it can now process the NOT EXISTS part quickly using the index instead of scanning the entire dbo.entity table. It is moving fast again.

How to check if a table valued parameter is empty or not inside of a where clause?

I am writing a function where I am passing a table valued parameter.
In some of the cases table valued parameter can be empty.
So, my function looks like below-
CREATE FUNCTION [dbo].[testTableValueParam]
(
#created_date datetime = null
,#Ids dbo.IdList readonly
)
RETURNS TABLE
AS
RETURN
(
SELECT top 10
name
,scores
,mgr_name
from dbo.employee
where
created_date = #created_date
and
employeeId in (select empid from #Ids) --ignore this condition when #Ids is empty.
)
My table type is created as below-
CREATE TYPE [dbo].[IdList] AS TABLE(
[empid] [nvarchar](11) NULL
)
I am calling my function from a c# code.
There are cases when the table value parameter will be empty and in those cases, when the table value parameter is empty, i want to ignore the condition in where clause.
I went through some of the links while searching my answer and the answers suggested in earlier posts didn't fix my problem.
So, right now, when #Ids parameter is empty, it gives me no record.
In some of the post they suggested not to pass a parameter for table value at all, and it will automatically treat it as an empty table.
But I have cases when I need to pass the parameter with data.
Some of the answers suggested, using if exist(select 1 from #Ids)
But, I can not use if exist in my where clause.
Please provide any suggestions.
Your responses are much appreciated.
Thanks.
You can use the NOT EXISTS operator something like....
CREATE FUNCTION [dbo].[testTableValueParam]
(
#created_date datetime = null
,#Ids dbo.IdList readonly
)
RETURNS TABLE
AS
RETURN
(
SELECT top 10
name
,scores
,mgr_name
from dbo.employee
where
(#created_date IS NULL OR created_date = #created_date)
and
(
NOT EXISTS (SELECT * FROM #Ids)
OR
employeeId in (select empid from #Ids)
)
)
CREATE FUNCTION [dbo].[testTableValueParam]
(
#created_date datetime = null
,#Ids dbo.IdList readonly
)
RETURNS TABLE
AS
RETURN
(
SELECT top 10
name
,scores
,mgr_name
from dbo.employee
where
created_date = #created_date
and
((select count(*) from #Ids) < 1 or employeeId in (select empid from #Ids))
employeeId in (select empid from #Ids) --ignore this condition when #Ids is empty.
)
Try this.
CREATE FUNCTION [dbo].[testTableValueParam]
(
#created_date datetime = null
,#Ids dbo.IdList readonly
)
RETURNS TABLE
AS
RETURN
(
IF EXISTS (SELECT 1 FROM #Ids)
BEGIN
SELECT TOP 10
name
,scores
,mgr_name
FROM dbo.employee
WHERE created_date = #created_date
AND employeeId in (select empid from #Ids) --ignore this condition when #Ids is empty.
END
ELSE
BEGIN
SELECT TOP 10
name
,scores
,mgr_name
FROM dbo.employee
WHERE created_date = #created_date
END
)

SQL Queue processing with UPDLOCK, READPAST and still getting deadlocks

The following stored procedure is working like a champ. This stored procedure facilitates queue processing for thousands of records and frequently fires simultaneously (sometimes threaded up to 7 or 8 times at any given instant):
Alter Procedure sp.QueueProcessSingle
As
DECLARE #aid int
update TABLE1 set [active] = 1, #aid = aid where aid=(
SELECT TOP 1 aid
FROM TABLE1 t1 WITH (UPDLOCK, READPAST)
WHERE
(t1.status is NULL) AND (t1.groupcount = 1) AND (t1.active = 0)
order by recorddate, recordtime)
update TABLE1 set status = 'Getting worked on' where #aid = aid
I'm happy with the above stored procedure, but it's its 1st cousin that I'm not happy with. A similar stored procedure needs to do the exact same thing as the sp above, only it does it across a group of records. In other words instead of just updating 1 record as "having work performed on it, therefore don't grab this record again when the stored procedure executes again" it needs to update 2 or 3 or 4 or more records that have similar data (same name and phone).
Here's how I have the bastard cousin sp now and I'm getting deadlocks:
Alter Procedure sp.QueueProcessMultiple
As
DECLARE #aid int
DECLARE #firstname nvarchar(50)
DECLARE #lastname nvarchar(50)
DECLARE #phone nvarchar(50)
update TABLE1 set active = 1, #aid = aid where aid=(
SELECT TOP 1 aid
FROM TABLE1 t1 WITH (UPDLOCK, READPAST)
WHERE
(t1.status is NULL) AND (t1.groupcount > 1) AND (t1.active = 0)
order by recorddate, recordtime)
UPDATE TABLE1 set status = 'Getting worked on' where #aid = aid
/****** Ok, now I have updated the "parent" record of the group which has the earliest date and time but now I also need to update the other records that are in this group*****/
SELECT #firstname = firstname, #lastname = #lastname, #phone = phone
FROM TABLE1 WITH (UPDLOCK, READPAST)
WHERE #aid = aid
UPDATE TABLE1 set status = 'Getting worked on', active = 1 where #firstname = firstname AND #lastname = lastname AND #phone = phone AND status is NULL AND active = 0
And as is often the case and part of the beauty of stackoverflow, just typing this up is shedding a bit of light on it for me. It seems like it would be better to update the whole group at once instead of just updating the "parent" record of the group and then updating all records in the table that have matching data of the parent record. Now how to do that? I'll keep looking and will post a solution if/when I get it, but any input would be much appreciated. Thanks!
Should be able to do this in one statement using DENSE_RANK to get the first aid.
This works if the collected rows all have the same (recorddate, recordtime)
Also, you need ROWLOCK too
update
t1
set
set status = 'Getting worked on',
active = 1,
#aid = aid, #firstname = firstname, #lastname = lastname, #phone = phone
FROM
(
SELECT
DENSE_RANK() OVER (ORDER BY recorddate, recordtime) AS rn,
aid, firstname, lastname, status, active
FROM TABLE1 t1x WITH (UPDLOCK, READPAST, ROWLOCK)
WHERE
(t1x.status is NULL) AND (t1x.groupcount > 1) AND (t1x.active = 0)
) t1;
WHERE
rn = 1

Resources