Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2008 Forums
 Transact-SQL (2008)
 Update table after Select statement

Author  Topic 

RoLYroLLs
Constraint Violating Yak Guru

255 Posts

Posted - 2009-06-19 : 14:01:56
Hi All,

I was told I would need to make a temp table in order to make this work. I have not yet had the need for a temp table, and in the past I've heard of performace issues, so I always tried to stay away.

I'll describe what I have and what results I need. My main question is how can this be done with sprocs and/or functions. I hope I don't need to use temp tables, but if I have to, I will.

Table:


create table [dbo].[users]
[userid] [int] identity(1,1) not null,
[username] [varchar](255) not null,
constraint [pk_users] primary key clustered


create table [dbo].[links]
[linkid] [int] identity(1,1) not null,
[linkname] [varchar](255) not null,
constraint [pk_links] primary key clustered


create table [dbo].[userlinks]
[userid] [int] not null,
[linkid] [int] not null,
[repeat] [int] not null


create procedure [dbo].[sp_GetLinks]
@userid int
as
begin
set nocount on;
--here i need to be able to add one to the repeat or add to the table if doesn't exist
--plus i don't know how to add one to repeat for each row selected
select top 10 *
from links
where linkid not in
(
select top 10 ul.linkid
from userlinks ul
where ul.userid = @userid
order by ul.repeat desc
)
order by (newid())
end



Users holds a list of registered users, while links are a list of about 1000 links we add. The idea is that the sproc should return 10 random links. These random links must not be any that have been return yet, and inserted into the userlinks table with the userid, linkid and a "repeat = 1". Obviously at some point all the links would have been returned, so then the goal is to return any 10 links and update the userlinks table and one to the repeat.

I am open to redesigning the db design if there is a better way to accomplish this.

Thank you in advance.


- RoLY roLLs
http://www.buysellmotors.com
http://www.tikiloveroom.com
http://www.rolyrolls.com
http://iphone.rolyrolls.com

webfred
Master Smack Fu Yak Hacker

8781 Posts

Posted - 2009-06-19 : 14:49:35
Let's assume you have 20000 registered users.
You get 10 random links using your query.
So the table userlinks has to get 20000 * 10 entries inserted -right?

But then, what is your goal?
Perhaps you want to have ten links and they are used for your users when visiting your homepage and the visitors shall not see the same link every time they are going to your homepage.

Then tell us please how the story will go on.
I think there are better ways - but I really don't know what is your goal with this stuff.

Greetings
Webfred


No, you're never too old to Yak'n'Roll if you're too young to die.
Go to Top of Page

RoLYroLLs
Constraint Violating Yak Guru

255 Posts

Posted - 2009-06-19 : 15:50:08
EDIT: I totally misunderstood your question, but I'll leave this to better understand my goal.

quote:
Originally posted by webfred


Perhaps you want to have ten links and they are used for your users when visiting your homepage and the visitors shall not see the same link every time they are going to your homepage.



It has nothing to do with links on the homepage, but the idea is exactly right. The user shall not see the same link everytime, unless they have already seen all the links, then they shall see 10 random links out of the entire collection of links. Thanks for your reply!


Going with your example: I have 20,000 users. Links are not related to users. Which means I'll have say 500 links today. and even if in a month I have 200,000 users, I'll still have 500 links (assuming I didn't insert any). I used the word "links" as an example, sorry to confuse, but it has nothing to do with user entries.

ADD:
-------------------
Of course, if all users make a request at least once, I would have 200,000 * 10 records in userlinks. Ewww! I will describe here in more detail, and like i said, I am willing to change my db design.
-------------------


The goal is when any one user retrieves 10 records from the table userlinks, they must NOT get any that they have retrieved so far. Here's a step-by-step.

STEP 1:
System goes live. 0 records in users table. 500 records in links table. 0 records in userlinks table.

STEP 2:
A new user registers. The user information will be added to the users table. (No problems here). 1 record in users table. 500 records in links table. 0 records in userlinks table.

STEP 3:
A user retrieves a list of records from the links table (currently limited to 10 records for this example). The query will grab 10 records in the list table at random. The query will check the userlinks table for any matches of both the userid field of the user logged in and the linkid being resulting from the select statement. Since this is the first time, the result would be 10 out of any of the 500 records. I need to keep track that the user was given these records. I store this in the userlinks table.

More detail: If the userid is "1", and the resulting recordset was linkids: 1,63,178,423,245,121,167,386,12,499. Then userlinks table would look like this:

select * from userlinks

userid linkid repeat
1 1 1
1 63 1
1 178 1
1 423 1
1 245 1
1 121 1
1 167 1
1 386 1
1 12 1
1 499 1


STEP 4:
The next time (doesn't matter when) the same user retrieves another 10 records, it can be any out of the 490 records that HAVE NOT been sent, since thay have already retreived 10. And these must be stored as in step 3. Obviously this requires to check the userlinks table and exclude any linksids sent prior.

STEP 4.5:
After the same user retrieves links 49 times, only 10 links would be left to send next time they are requested. (MATH: 10 records recieved 49 times = 10 * 49 = 490. 500 total records - 490 already sent = 10).

STEP 5:
The same user (with userid = 1) makes another request and the last 10 records available in the links table are sent.

NOTE: do not confused "last 10 records" being records 491 - 500. Keep in mind prior, that records are retrieved in random order.

STEP 6:
The same user makes another request. This is the 51st time. Because all the records have been sent to the user once. The query would retrieve 10 random records out of all the 500 records. Those 10 records would then have at repeat field of 2 and should not be retrievable on the user's next request.

I hope this helps. Also, keep in mind that records may be added to the links table at anytime (by admins), so say on the 50th time the user makes a request and there are had 503 records in links. If the same user once again makes a request, he would be receiving 10 out of the 13 records. Remember my math, by this time the user has seen 490 records. Since there are 503 records now, 503 - 490 = 13. This also present this possibility. On the 52st request, the user must get the 3 records left, plus any 7 records in the whole table.

Wow! I hope I did not make it more confusing.

If I did, sorry. Just ask, and I'll try to clarify.

- RoLY roLLs

http://www.buysellmotors.com
http://www.tikiloveroom.com
http://www.rolyrolls.com
http://iphone.rolyrolls.com
Go to Top of Page

daxbuddy
Starting Member

1 Post

Posted - 2009-06-19 : 17:39:16
If I understand the underlying need, you need to find a set of linkIDs that are not currently associated with the passed UserID.

The randomness of those links (top 10 isn't necessarily random) can be done in several ways. I leave that to you.

However, the same set of links needs to be available as the return set from your procedure. So you have two functions required - one performs a select, the other an insert (or when you start to run out of links and need to increment your Repeat column, an update).

To keep this data persistent for both functions and avoid a second query, a temp table or (better) table variable would be the obviously way to handle it from my experience. Is there some reason you don't want to use a table variable?

i.e. simplified pseudocode

Declare @found_links Table
(linkid)

Insert into @found_links
Select top 10 linkID
FROM .... (your prior query would work)

Insert into UserLinks
(userid, linkid, repeat)
select @passedUserid, linkid, 1 -- '1' assumes first time use
from @found_links

-- return set
select * from @found_links
Go to Top of Page

RoLYroLLs
Constraint Violating Yak Guru

255 Posts

Posted - 2009-06-19 : 18:14:01
quote:
Originally posted by daxbuddy

Declare @found_links Table
(linkid)

Insert into @found_links
Select top 10 linkID
FROM .... (your prior query would work)

Insert into UserLinks
(userid, linkid, repeat)
select @passedUserid, linkid, 1 -- '1' assumes first time use
from @found_links

-- return set
select * from @found_links



Ok I'll try that out when I get a chance.

I never liked temp tables, and I guess never really delved into them since I haven't needed them. I just learned about variable tables a few minutes ago. But like i said, if i have to I will. My Initial attempt was when the user made a request, the query itself called a function which sent the linkid and the userid, and did the update/add there. And then I got a warning about insert/update can't be done inside a function . See below:


create function [dbo].[udf_CheckUserLink]
(
@userid int
,@linkid int
)
return int
as
begin

declare @result int
@userlinkid int
,@repeat int

select @userlinkid = ul.linkuserid, @repeat = ul.repeat
from userlinks ul
where ul.linkid = @linkid and ul.userid = @userid

if isnull(@userlinkid,0) > 0
begin
--record found. UPDATE
select @repeat = @repeat + 1

update userlink
set repeat = @repeat
where userlinkid = @userlinkid
end
else
begin
select @repeat = 1

insert userlink (userid, linkid, repeat)
values (@userid, @linkid, @repeat)
end
end

select @result = @repeat

return @result
end


create procedure [dbo].[sp_GetLinks]
@userid int
as
begin
set nocount on;
--here i need to be able to add one to the repeat or add to the table if doesn't exist
--plus i don't know how to add one to repeat for each row selected
select top 10 links.linkid,links.linkname,[dbo].[udf_CheckUserLink](@userid,links.linkid) as [repeat]
--i don't really need the repeat value, but decided why not send it
from links
where linkid not in
(
select top 10 ul.linkid
from userlinks ul
where ul.userid = @userid
order by ul.repeat desc
)
order by (newid())



I even converted the update and the insert statements into seperate stored procedures and called them from the fuction and got "Only functions ans some extended stored procedures can be executed from within a function." Hehe...opps!

- RoLY roLLs

http://www.buysellmotors.com
http://www.tikiloveroom.com
http://www.rolyrolls.com
http://iphone.rolyrolls.com
Go to Top of Page

RoLYroLLs
Constraint Violating Yak Guru

255 Posts

Posted - 2009-06-20 : 00:35:37
Hey daxbuddy,

That variable table worked out GREAT (from what I can tell so far)!

Here's what I got so far:


Declare @found_links Table
(linkid int)

insert into @found_links
select top 10 l.linkID
from links l
order by
isnull(
select top 1 ul.linkid
from userlink ul
where ul.userid = @userid and ul.linkid = l.linkid
order by ul.repeat asc
,0) asc --this can probably be a function, if it will improve performance. please advise
, newid() --newid() helps grab id's in random order
)

select *
from links
where linkid in (select * from @found_links)
order by newid()

For example sake...I have 15 links, 1 user, and 10 records in userlinks.


userlinks table

userid linkid repeat
1 1 1
1 2 1
1 3 1
1 4 1
1 5 1
1 6 1
1 7 1
1 8 1
1 9 1
1 10 1


So far what I got seems to return a random order of records from the links table, and it's always 10 records (required to always be 10). Also since I am not yet updating/adding records for testing purposes, it ALWAYS returns linkid records 11-15, and another 5 random from the remaining 10 which have a repeat of 1.

My next task is how to insert any record in the variable table not yet in the userlinks table, and how do I update the repeat field for those records that are already there.

My assumption:
Make another variable table with a similar method to extract the ids which exist, the use that to do a select update. I think, however I may be able to accomplish this via joins? Some help would be great.

Also, if anyone see's if any of my queries can use optimization like use a function for one piece, or using inner joins instead of what I did, I would love to hear from you. I lack much optimization tips/tricks/standards.

- RoLY roLLs

http://www.buysellmotors.com
http://www.tikiloveroom.com
http://www.rolyrolls.com
http://iphone.rolyrolls.com
Go to Top of Page
   

- Advertisement -