logo logo

 Back to main page

The NWNX Community Forum

 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 
 
SQLExecDirect() within while(SQLFetch())

 
Post new topic   Reply to topic    nwnx.org Forum Index -> General Discussion
View previous topic :: View next topic  
Author Message
weldieran



Joined: 05 Aug 2005
Posts: 71

PostPosted: Fri Oct 21, 2005 2:20    Post subject: SQLExecDirect() within while(SQLFetch()) Reply with quote

OK - So i've now come across two different situations where I would need to do something like this
Code:

    string sAutoid;
    SQLExecDirect("SELECT autoid FROM thistable");
    while (SQLFetch() == SQL_SUCCESS)
    {
        sAutoID = SQLGetDataString(1);
        SQLExecDirect("SELECT value FROM thistable WHERE relatedID="+sAutoID);
        if (SQLFetch() == SQL_SUCESS)
            return TRUE;
    }


But its not possible as APS stands because the SQLExecDirect() function wipes out the previous SQLFetch() loop.

Papillon! My man! hehe - Know some way I can update my APS to make this possible?
Back to top
View user's profile Send private message
dguntner



Joined: 31 Dec 2004
Posts: 116

PostPosted: Fri Oct 21, 2005 4:53    Post subject: Reply with quote

That looks a bit convoluted. Smile What exactly is it that you're trying to do? And can you show what your table layout is for "thistable?"

--Dave
Back to top
View user's profile Send private message
weldieran



Joined: 05 Aug 2005
Posts: 71

PostPosted: Fri Oct 21, 2005 5:56    Post subject: Reply with quote

I know there are ways around what I want to do.. i went around the first time i ran into this...

but running into it again makes me think about modifying nwnx so that it can handle more than one query at a time.

the first scenario, which i've already worked around, was a custom item storage system. If you put a bag into the storage system, it marked the item as "in a bag" then uses the bags "id number" so that when it loads the items back up from database, it puts stuff in the rightplace.

The first query checks for all "noninside" bag items
then inside that query, when it comes across a bag, it would want to run the query that loads the items for that bag.


ya sure, more ways to do any one thing - but in both these situations i've come across, it would've been best to be able to run another query without wiping out yer first query.
Back to top
View user's profile Send private message
dguntner



Joined: 31 Dec 2004
Posts: 116

PostPosted: Fri Oct 21, 2005 6:17    Post subject: Reply with quote

That's not quite what I meant. Smile The way you're doing your sample queries there looks kinda like you'd be looking something up in one table and then comparing that against something in a second table, but your example only references one (the same) table. That's why I was asking for your table structure, so that I can try to grasp better what it is you're trying to do.

--Dave
Back to top
View user's profile Send private message
Papillon
x-man


Joined: 28 Dec 2004
Posts: 1060
Location: Germany

PostPosted: Sat Oct 22, 2005 22:32    Post subject: Reply with quote

Many times you should be able to use a join instead of nested loops, which are more efficient as well.
_________________
Papillon
Back to top
View user's profile Send private message Visit poster's website MSN Messenger
weldieran



Joined: 05 Aug 2005
Posts: 71

PostPosted: Sat Oct 22, 2005 23:11    Post subject: Reply with quote

autoid | userid | playerid | resref | identified | stacked | container_id | in_container_id | resourcecount | pickpocketable | stolen

There's the table.

Now - this is for persistant storage and utilizes my custom player persistence system (thus the userid, playerid).

When storing the items, and it comes across a bag, it marks the bag with an ID number (1st bag, 2nd bag, 3rd bag, etc). Then when it comes across the items inside that bag, it stores the item with the appropriate ID number of the bag. All works great.

When querying for the data - i have to do all kinds of funky moves to work around the fact that nwnx/aps doesn't support more than 1 query at a time.

What I would've LIKED to do is:
Code:

object oCreated;
object oCreatedInside;
SQLExecDirect("SELECT * FROM storage WHERE userid = 1 AND in_container_id = 0");
while (SQLFetch() == SQL_SUCCESS)
{
    oCreated = CreateItemOnObject(SQLGetData(4), bla bla);
    if (SQLGetData(5) == "1")
        SetIdentified(oCreated, TRUE);
    ...blablalblalbla
    .......
    if (SQLGetData(7) != "0") //this is the container_id field
    {
        SQLExecDirect("SELECT * FROM storage WHERE userid = 1 AND in_container_id = "+SQLGetData(7));
        while (SQLFetch() == SQL_SUCCESS)
        {
            oCreatedInside = CreateItemOnObject(SQLGetData(4), oCreated);
            ... bla bla bla
        }
    }

}


Now, of course this is extremely oversimplified - but it should get the idea across. There are actually many custom functions involved in the decoding of items from the database.. but when it comes across a container, it will process the DecodeContainer - which then does a query to the DB for the items inside the container.

The way i worked around it was running the while loop over the initial query and storing that recordset into a pseudoarray of localvars - then i loop thru the pseudoarray so that when/if it cross a container, the SQLExec in the DecodeContainer wont mess it up.

Rolling Eyes
Back to top
View user's profile Send private message
monezz



Joined: 03 Jan 2005
Posts: 52
Location: The Netherlands

PostPosted: Tue Oct 25, 2005 14:34    Post subject: Reply with quote

I defined a test table called recur to simulate the problem.

mysql> select * from recur order by ifnull(con_id, in_con_id) asc, con_id desc;
+----+--------+-----------+
| id | con_id | in_con_id |
+----+--------+-----------+
| 1 | NULL | NULL |
| 2 | NULL | NULL |
| 3 | NULL | NULL |
| 4 | 1 | NULL |
| 14 | NULL | 1 |
| 9 | NULL | 1 |
| 8 | NULL | 1 |
| 7 | NULL | 1 |
| 6 | NULL | 1 |
| 5 | NULL | 1 |
| 15 | NULL | 1 |
| 10 | 2 | NULL |
| 11 | NULL | 2 |
| 12 | NULL | 2 |
| 13 | NULL | 2 |
+----+--------+-----------+

pseudo code for processing:
id is resref for ease..
Code:

int id;
int con_id;
int in_con_id;

object target = getPC(); // first target will be the pc's inventory, or some other inventory

while (SQLFetch() == SQL_SUCCESS)
{
  id = StringToInt(SQLGetData(1));
  con_id = StringToInt(SQLGetData(2)); -- null becomes 0, so dont use 0 as meaningfull id
  in_con_id = StringToInt(SQLGetData(3));

  if (con_id != 0)
  {
     // create the bag and set the target to it so next items will be created inside
     target = createItemOnObject(id,target);
  }else
  {
     // item is not a bag so create item on the target
     createItemOnObject(id,target);
  }

}
Back to top
View user's profile Send private message Visit poster's website
Asmodae



Joined: 07 Jan 2005
Posts: 55

PostPosted: Tue Nov 22, 2005 0:24    Post subject: Reply with quote

This should really be two, or most likely three different tables. Then sorting out which is where would be trivial. Since the post is old and the poster hasn't checked in a while I won't get into it, but if you're interested I'd be happy to detail the process for you.

-Asmodae
_________________
Nepenthe - An NWN2 Persistant World, coming to a planet near you. http://www.nepentheonline.com
Back to top
View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger
weldieran



Joined: 05 Aug 2005
Posts: 71

PostPosted: Thu Dec 01, 2005 6:31    Post subject: Reply with quote

In the end, I've just modified the nwnx plugin to allow "channels" of data. Then modified aps to pass the "channel" parameter and wha-la!

But then it just ends up being too many instructions to reload a person's storage as there are quite a few many things I tried to track on items being put in... and all those things need checked and readded to the item coming out of storage *sigh* TMI's, i hate em.
Back to top
View user's profile Send private message
Asmodae



Joined: 07 Jan 2005
Posts: 55

PostPosted: Thu Dec 01, 2005 23:05    Post subject: Reply with quote

Yeah, TMI's are annoying, especially when you think you shouldn't be getting them, or you are willing to take the performance hit for that function...

But in the end they keep NWServer running and generally keep people from writing code that soaks too much cpu time.

btw: if you construct your tables and queries properly not only can you completely avoid TMI's in cases like this, but make the process orders of magnitude faster.
_________________
Nepenthe - An NWN2 Persistant World, coming to a planet near you. http://www.nepentheonline.com
Back to top
View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger
weldieran



Joined: 05 Aug 2005
Posts: 71

PostPosted: Fri Dec 02, 2005 0:59    Post subject: Reply with quote

ya, i'm no expert on database design... i've designed plenty, but in no way would I claim my way is right and someone elses is wrong, hehehe. I'm just a code cruncher.

Wanna explain your basic theory on DB design?
Back to top
View user's profile Send private message
Asmodae



Joined: 07 Jan 2005
Posts: 55

PostPosted: Fri Dec 02, 2005 20:54    Post subject: Reply with quote

In doing research for some of my more complex system design I had to learn a lot about how databases work, or at least a bit of theory about how to design them. I spent time on a lot of different tutorials puzzling things out, but here's a few key concepts.

Concept 1: A table should describe a type of entity or object completely, preferably only one type. Like a class in OOP. A row in a table is a description of a specific instance of that type.

Say you want a characters table with rows:
id | name | playersname | lastlogin

where the id column is your primary key.

Concept 2: Data Normilization, I don't recall enough off the top of my head to put this down coherently but it involves making sure the design of your tables follows some guidelines to ensure that the relations between your data are properly modeled. Therefore your queries can return the data you need when you need it, without fancy lookup tricks.

Google "SQL tutorial" for some tips on queries. Will post some links and an example in a bit.
_________________
Nepenthe - An NWN2 Persistant World, coming to a planet near you. http://www.nepentheonline.com
Back to top
View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger
Asmodae



Joined: 07 Jan 2005
Posts: 55

PostPosted: Fri Dec 02, 2005 22:55    Post subject: Reply with quote

I'm having some difficulty coming up with good examples based on your posts (let me know what your data and tables look like if you want more specifics), so I'll give you some from my experience.

We needed a table for character stuff, which is much like above, a table for player data, actual player info based on public cd key, and some quest data.

The first two tables are easy, here are simplified versions.

table: playerdata
playerid | cdkey | lastlogin

table:chardata
id | playerid | name | lastlocation | lasthp


The quest table was a bit trickier. The thing is, one player can have many different quests, and one quest can be done by many different players. This is a many-to-many relationship and to model properly requires and intermediary table. So we had the following table for information about the quest.

table quests:
id | questname | questnpc | description | stage1 | stage2 | stage3


Up untill now each table describes its own type of data entit, each one has an autogenerated unique id field. Now we need a table that tells us which characters have done which quests.

table questprogress:
charid | questid | complete1 | complete2 | complete3

The key here is that this table doesn't have an autogenerated unique id field. The primary key of this table is the combination of the first two fields charid and questid. Each row is uniquely described by these two fields together. In essence this is what makes up a 'questprogress' entity. The character that is doing the quest, and the quest the character is doing. The extra fields hold some data about whether each stage of the quest is complete. But my script looks back to the actual quest table if it needs to know ABOUT the quest itself. Each stage column in the quests table has a description of that stage. (again our real system is much more complex).

Now we need to get some data from our tables. To find out if stage 1 is complete for a quest:

SELECT complete1 FROM quests,chardata,questprogress WHERE quests.id = questprogress.questid AND charadata.id = questprogress.charid AND quests.id = <id of quest> AND chardata.id = <id of character>

The tables are joined on their respective id's, and we select from that resulting joined data set, the quest id and character id we want. This SELECT statement should return a single record.

The key to making this work is the 'linking' table that uses the combined id's. This saved me from having to create looping and searching code in my database. The data in each table is very much part of that object or entity, which helps make sure that we can write queries for anything we want without special code. If this doesn't apply to you, let me know what your tables and data actually are so I can provide a few more specific tips.

Lastly, I am by no means an SQL guru having picked up everything I know from looking at other people's code and web tutorials. If I've said anything grossly inaccurate (or even slightly) someone please correct me.
_________________
Nepenthe - An NWN2 Persistant World, coming to a planet near you. http://www.nepentheonline.com
Back to top
View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger
Asmodae



Joined: 07 Jan 2005
Posts: 55

PostPosted: Fri Dec 02, 2005 23:28    Post subject: Reply with quote

heh, going through the posts I found the one where you present your table structure. You stated it is this:
autoid | userid | playerid | resref | identified | stacked | container_id | in_container_id | resourcecount | pickpocketable | stolen

The first thing I see is that you should probably have a seperate table for item data, although I don't think that would solve your problem, you would basically come to the simplified table that monezz posted, and his code looks like it would work for that.

As for TMI's, its hard to see everthing that's being done in your code for items. But if you use SCO/RCO you shouldn't need to track anything as I believe the object is saved outright, including variables, settings, flags, etc.
_________________
Nepenthe - An NWN2 Persistant World, coming to a planet near you. http://www.nepentheonline.com
Back to top
View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger
weldieran



Joined: 05 Aug 2005
Posts: 71

PostPosted: Sat Dec 03, 2005 1:15    Post subject: Reply with quote

unfortunately, to my knowledge, linux version of nwnX and its plugins dont contain the ability to actually store objects. And there's no way I would ever run windows as the server (yes, i'm a M$ hater, hehe)

So ya, gotta track the flags and variables and recreate the objects from the resref's. It aint perfect, and i dont much like it, but i've not even a clue how to modify the DB plugin to store objects - that much is beyond me Razz

I wish there was as much support/development for the linux version as there is for the windows version Confused but i'm still very grateful there are the basics available for linux Very Happy
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    nwnx.org Forum Index -> General Discussion All times are GMT + 2 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group