SQLserver Store Column as variable and loop through it - sql-server-2008

I am still pretty new to SQL server and I am not sure how to do this. I am first creating a table with just the IDs I need:
SELECT DISTINCT
ID_NUMBER
INTO
#IDlist
FROM
V_Rpt_IDs WITH (NOLOCK)
WHERE
ID_NUMBER in (
'1000764169'
,'1005870537'
,'1008053856'
,'1008054376'
,'1008410224'
,'1008411317'
,'1008465318'
,'1008466074'
,'1008492967'
,'1010546872'
,'1010554301')
Select * from #IDlist
And this works fine. But now I would like to declare a variable to represent this column, or each item in this column, so that I can then do a loop where it loops through each ID Number and returns information about each one and then presents all of that as a table. Here is my shot at that:
Declare #IDNumber as VARCHAR(10)
Set #IDNumber = #IDlist.ID_NUMBER
DECLARE #cnt INT = 0
WHILE #cnt < (Select Count(*) From #IDlist)
BEGIN
SELECT TOP 1
NAME
,MAILING_ADDRESS_1
,MAILING_ADDRESS_CITY
,MAILING_STATE
,MAILING_ZIP
from
V_Rpt_Info
WHERE
ID_NUMBER = #IDNumber
SET #cnt = #cnt + 1
END
DROP TABLE #IDlist
But when I Set the #IDNumber variable to #IDlist.ID_NUMBER, it says The multi-part identifier "#IDlist.ID_NUMBER" could not be bound.
How do I do this?
Thanks

The way you set the variable is not correct, SQL doesn't know which ID_NUMBER row it should assign to the #IDNumber variable.
You should do this with a SELECT, for example
SET #IDNumber = SELECT TOP 1 ID_NUMBER FROM #IDlist
But, why would you like to loop through this temporary table this way ? Isn't it possible to join the necessary data with this table instead of doing it one by one ?

Rather then loop through, you're going to want to join your ID table to your V_Rpt_Info view.
SELECT
NAME
, MAILING_ADDRESS_1
, MAILING_ADDRESS_CITY
, MAILING_STATE
, MAILING_ZIP
FROM V_Rpt_Info V
INNER JOIN #IDlist ID
ON V.ID_NUMBER = ID.ID_NUMBER

Related

SQL Server T-SQL breaking a string into a temp table for a join

We have a SQL Server Scalar Function and part of the process is to take one of the input values and do the following
'inputvalue'
Create a table variable and populate with the following rows
inputvalue
inputvalu
inputval
inputva
inputv
input
inpu
inp
Then this table is joined to a query, ordered by len of the inputvalue desc and returns the top 1. The actual code is here
DECLARE #Result NVARCHAR(20);
DECLARE #tempDialCodes TABLE (tempDialCode NVARCHAR(20));
DECLARE #counter INT = LEN(#PhoneNumber);
WHILE #counter > 2
BEGIN
INSERT INTO #tempDialCodes(tempDialCode) VALUES(#PhoneNumber);
SET #PhoneNumber = SUBSTRING(#PhoneNumber, 1, #counter - 1);
SET #counter = #counter - 1;
END
SET #Result = (SELECT TOP 1 [DialCodeID]
FROM DialCodes dc JOIN #tempDialCodes s
ON dc.DialCode = s.tempDialCode
ORDER BY LEN(DialCode) DESC);
RETURN #Result
It works fine but I am asking if there is a way to replace the while loop and somehow joining to the inputvalue to get the same result. When I say it works fine, it's too dam slow but it does work.
I'm stumped on how to break up this string without using a loop and to a table variable but my warning light tells me this is not efficient for running against a table with a million rows.
Are you familiar with tally tables? The speed difference can be incredible. I try to replace every loop with a tally table if possible. The only time I haven't been able to so far is when calling a proc from within a cursor. If using this solution I would recommend a permanent dbo.Tally table with a sufficiently large size rather than recreating every time in the function. You will find other uses for it!
declare #PhoneNumber nvarchar(20) = 'inputvalue';
declare #tempDialCodes table (tempDialCode nvarchar(20));
--create and populate tally table if you don't already a permanent one
--arbitrary 1000 rows for demo...you should figure out if that is enough
--this a 1-based tally table - you will need to tweak if you make it 0-based
declare #Tally table (N int primary key);
insert #Tally
select top (1000) row_number() over (order by o1.object_id) from sys.columns o1, sys.columns o2 order by 1;
--select * from #Tally order by N;
insert #tempDialCodes
select substring(#PhoneNumber, 1, t.N)
from #Tally t
where t.N between 3 and len(#PhoneNumber)
order by t.N desc;
select *
from #tempDialCodes
order by len(tempDialCode) desc;

SQL: GROUP BY Clause for Comma Separated Values

Can anyone help me how to check duplicate values from multiple comma separated value. I have a customer table and in that one can insert multiple comma separated contact number and I want to check duplicate values from last five digits.For reference check screenshot attached and the required output is
contact_no. count
97359506775 -- 2
390558073039-- 1
904462511251-- 1
I would advise you to redesign your database schema, if possible. Your current database violates First Normal Form since your attribute values are not indivisible.
Create a table where id together with a single phone number constitutes a key, this constraint enforces that no duplicates occur.
I don't remember much but I will try to put the idea (it's something which I had used a long time ago):
Create a table value function which will take the id and phone number as input and then generate a table with id and phone numbers and return it.
Use this function in query passing id and phone number. The query is such that for each id you get as many rows as the phone numbers. CROSS APPLY/OUTER APPLY needs to be used.
Then you can check for the duplicates.
The function would be something like this:
CREATE FUNCTION udf_PhoneNumbers
(
#Id INT
,#Phone VARCHAR(300)
) RETURNS #PhonesTable TABLE(Id INT, Phone VARCHAR(50))
BEGIN
DECLARE #CommaIndex INT
DECLARE #CurrentPosition INT
DECLARE #StringLength INT
DECLARE #PhoneNumber VARCHAR(50)
SELECT #StringLength = LEN(#Phone)
SELECT #CommaIndex = -1
SELECT #CurrentPosition = 1
--index is 1 based
WHILE #CommaIndex < #StringLength AND #CommaIndex <> 0
BEGIN
SELECT #CommaIndex = CHARINDEX(',', #Phone, #CurrentPosition)
IF #CommaIndex <> 0
SELECT #PhoneNumber = SUBSTRING(#Phone, #CurrentPosition, #CommaIndex - #CurrentPosition)
ELSE
SELECT #PhoneNumber = SUBSTRING(#Phone, #CurrentPosition, #StringLength - #CurrentPosition + 1)
SELECT #CurrentPosition = #CommaIndex + 1
INSERT INTO #UsersTable VALUES(#Id, #PhoneNumber)
END
RETURN
END
Then run CROSS APPLY query:
SELECT
U.*
,UD.*
FROM yourtable U CROSS APPLY udf_PhoneNumbers(Userid, Phone) UD
This will give you the table on which you can run query to find duplicate.

Loop through table variable and insert missing rows into new table variable

I have a table variable which is populated with a number of rows. What I am trying to achieve, is to loop through each row of the table variable, see if the ID column exists in another standard table and add that row to a newly created table variable.
My code is as follows:
DECLARE
#Intervention_ID int
SET #Intervention_ID = 969
/*---Check if intervention has pre-requisites---*/
DECLARE #PreRequisites TABLE
(
[Intervention_ID] int
)
INSERT INTO #PreRequisites
(
[Intervention_ID]
)
SELECT IP.[Requisite_ID]
FROM DI_Intervention_Prerequisites IP
WHERE
IP.[Intervention_ID] = #Intervention_ID
AND IP.[Prerequisite] = 1
/*---Check if pre-requisites have been completed---*/
DECLARE #Result TABLE
(
[Type_ID] int
, [Type_Name] nvarchar(max)
, [Intervention_ID] int
, [Intervention_Name] nvarchar(max)
)
WHILE NOT EXISTS
(
SELECT TOP 1 1
FROM DI_Employee_Intervention EI
WHERE
EI.[Intervention_ID] = (SELECT [Intervention_ID] FROM #PreRequisites)
)
INSERT INTO #Result
(
[Type_ID]
, [Type_Name]
, [Intervention_ID]
, [Intervention_Name]
)
As you can see, I am stuck at the WHILE NOT EXISTS part of the code. What needs to happen is for each row within #PreRequisites that does not exist in DI_Employee_Intervention, that specific #PreRequisites row need to be inserted into #Result.
Why don't you just use a set-based approach instead of the RBAR (row-by-agonizing-row) procedural approach you have now??
Something like:
INSERT INTO #Result([Type_ID], [Type_Name], [Intervention_ID], [Intervention_Name])
SELECT
... (some columns to match the columns of #Result)......
FROM
dbo.DI_Intervention_Prerequisites IP
WHERE
IP.[Intervention_ID] = #Intervention_ID
AND IP.[Prerequisite] = 1
AND NOT EXISTS (SELECT *
FROM DI_Employee_Intervention EI
WHERE EI.[Intervention_ID] = IP.[Intervention_ID] )
or something like that (I didn't quite understand all your intermediate steps and why you take them....)

MySql Stored Proc - Select query returns null if using where clause not included in select list?

I have a proc that simply contains the following:
SELECT Col1, Col2 FROM table WHERE Id = 1;
This always returns null values. If I change the statement to:
SELECT Id, Col1, Cold2 FROM table WHERE Id = 1;
The row gets returned as expected. Does mySql procs demand that the where clause columns appear in the select list? It doesn't have this behaviour when running the SQL directly in phpMyAdmin.
Here's the code in question:
CREATE PROCEDURE sp_player_login
(
IN
userGraphId INT(11),
authToken TEXT
)
BEGIN
DECLARE playerId INT;
DECLARE newPlayer BOOLEAN DEFAULT FALSE;
SELECT Id INTO playerId FROM player WHERE FacebookGraphId = userGraphId;
If playerId IS NULL THEN
-- Create the player.
SET newPlayer = TRUE;
-- Get new player defaults.
SELECT NewPlayerTurns, NewPlayerMoney, NewPlayerMorale, NewPlayerMissilePower FROM defaults WHERE Id = 1;
END IF;
END #
The second SELECT NewPlayerTurns... is the one that returns null values if I don't specify the Id column in the select clause. If I remove the first SELECT Id INTO ...the second query works without the Id in the select clause...? confused

SQL Cursor returning many number of tables

I have a sql stored proc where I use a cursor that contains a set of id's from a select statement and I use these id's going over one by one using the cursor to get values into other variables and use those variables to do sql joins .My problem is when I execute this I get many tables returned whereas I need just one table returned.
SET NOCOUNT ON
declare #BSVal as int
declare #GSVal as int
declare #mID as int
declare #qID as int
DECLARE M_Cursor cursor for
select
ms.MID,ms.QID
from vM As ms join QS as qs
ON ms.QSID=qs.QIDjoin
Mar as mar on mar.MarID=qs.MarID
where (ms.Cid='Web')
open M_Cursor
FETCH NEXT FROM M_Cursor
INTO #mID, #qID
--Get values
WHILE ##FETCH_STATUS = 0
BEGIN
set #BSVal= (select top 1 SCID from vSC where (EnID in
(select EnID from En where EnName='BAIDU')
and QTID=1 and MID=#mIDand QSID=#qID)order by ITime desc);
set #GSVal= (select top 1 SCID from vSC where ( EnID in
(select EnID from En where EnName='GRAPHIC') and QTID=1
and MID=#mIDand QSID=#QSID) order by ITime desc);
select * from
vM m
join vw5TABLE BNDCG on (m.QSid=BNDCG.QID And BNDCG.Position=1)
join vw5TABLE GNDCG on (m.QSid=GNDCG.QID And GNDCG.Position=1)
where
BNDCG.SCid=#BSVal
and GNDCG.SCid=#GSVal
and BNDCG.QSID=# qID
and GNDCG.QSID=# qID
and m.MID=#mID
FETCH NEXT FROM M_Cursor
INTO #MID, #QSID
END
CLOSE M_Cursor;
DEALLOCATE M_Cursor;
That code will run a select for each iteration of the cursor, which makes it look like 'many tables'. It sounds like you need to insert the results of that select into a temp table or table variable inside the cursor, then once the cursor is complete, select once from that temp table. I have not gone over your code in detail but I'm guessing a cursor may not be required for this.
Here's a rough sample using a table variable.
DECLARE #temptable TABLE (col1 INT, Col2 VARCHAR(3), Col3 INT)
insert into #temptable (col1,col2,col3)
select (col1,col2,col3) from
vM m
join vw5TABLE BNDCG on (m.QSid=BNDCG.QID And BNDCG.Position=1)
join vw5TABLE GNDCG on (m.QSid=GNDCG.QID And GNDCG.Position=1)
where
BNDCG.SCid=#BSVal
and GNDCG.SCid=#GSVal
and BNDCG.QSID=# qID
and GNDCG.QSID=# qID
and m.MID=#mID
....
..
DEALLOCATE M_Cursor;
SELECT Col1,Col2,Col3 FROM #temptable