1

I just started working with the EVE static dump, which is just a lot of tables with data about the game, such as a list of what solar systems connect, which is what I'm dealing with. I want to make a webpage that lets you filter out systems, and the first step is getting a list of systems nearby, with the distance to them.

I found a script that does it for MSSQL

--By Joanna Davaham http://forum.eveuniversity.org/viewtopic.php?t=44601&p=396107#p424943
--set values
DECLARE @jumpsAway INT =10
DECLARE @MiddleSystemName VARCHAR(50) = 'Aldrat'
DECLARE @Level INT =1

IF OBJECT_ID('tempdb..#map') IS NOT NULL
    DROP TABLE #map

CREATE TABLE #map
(fromSolarSystemID INT, toSolarSystemID INT, Level INT)



INSERT INTO #map
SELECT -1, mSS.solarSystemID, 0 FROM mapSolarSystems mSS
WHERE mSS.solarSystemName= @MiddleSystemName

WHILE @Level <= @jumpsAway
BEGIN


INSERT INTO #map
SELECT mSSJ.fromSolarSystemID, mSSJ.toSolarSystemID, @Level FROM mapSolarSystemJumps mSSJ
WHERE mSSJ.fromSolarSystemID IN (SELECT toSolarSystemID FROM #map WHERE Level = @Level-1)
AND  mSSJ.fromSolarSystemID NOT IN (SELECT fromSolarSystemID FROM #map)


SET @Level=@Level+1
END


SELECT m.*, mSS.solarSystemName, mSS.security  FROM #map m
JOIN mapSolarSystems mSS ON m.toSolarSystemID=mSS.solarSystemID
--WHERE mSS.security<0.45  --uncomment to check all nearby lowsec system

I know that I could probably just use the MSSQL version of the dump, but I also want to be learning more about how to use PostgreSQL better.

I understand what it's doing and everything, but I just don't understand PL/pgSQL well enough to make it work.

My attempt is

CREATE FUNCTION near(VARCHAR, INTEGER) RETURNS TABLE(fromID INT,toID INT,jumps INT,name VARCHAR,security VARCHAR) AS $$
DECLARE --Declaration from here http://www.postgresql.org/docs/9.1/static/plpgsql-declarations.html
    MiddleSystemName ALIAS FOR $1;
    jumpsAway ALIAS FOR $2;
    jumps INTEGER :=1;
BEGIN

    --http://stackoverflow.com/questions/11979154/select-into-to-create-a-table-in-pl-pgsql
    CREATE TEMP TABLE map AS
        SELECT -1, mSS.solarSystemID, 0
        FROM mapSolarSystems mSS
        WHERE mSS.solarSystemName= MiddleSystemName;

    LOOP

        --http://www.postgresql.org/docs/9.1/static/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN
        --If you don't do it with execute, you can only do one row, I guess?
        EXECUTE 'SELECT
       ||        mSSJ.fromSolarSystemID,
       ||        mSSJ.toSolarSystemID,
       ||        $1
       ||    FROM
       ||        mapSolarSystemJumps mSSJ
       ||    WHERE
       ||        mSSJ.fromSolarSystemID EXISTS (SELECT toSolarSystemID FROM map WHERE jumps = $1 - 1)
       ||        AND mSSJ.fromSolarSystemID NOT EXISTS (SELECT fromSolarSystemID FROM map)'
        INTO map
        USING jumps;



        jumps := jumps + 1
        EXIT WHEN jumps > jumpsAway;
    END LOOP;
    RETURN QUERY SELECT m.*,mSS.solarSystemName, mSS.security FROM JOIN mapSolarSystems mSS ON m.toSolarSystemID = mSS.solarSystemID;
END;
$$ LANGUAGE plpgsql;

And the error that produces is

Error is
ERROR:  "map" is not a known variable
LINE 27:         INTO map
                      ^

Thanks for all the help.

mtfurlan
  • 1,024
  • 2
  • 14
  • 25
  • Can you be more specific than `a thing`? Can you copy the relevant snippets into the question to make it useful to people without having to go to the pastebin link? [What have you tried?](http://mattgemmell.com/2008/12/08/what-have-you-tried/) – Ryan Gates Jun 10 '13 at 19:52
  • I don't think it mattes what I'm trying to do with this, but the thing I'm trying to make is something to let me filter by systems near a starting system. The first step in that is to get a list of the systems nearby, and how far away they are. I will edit it so the code is here, sorry about that. And what I tried was was the second link. – mtfurlan Jun 10 '13 at 23:59

1 Answers1

1

PL/pgSQL

This should be a valid translation to plpgsql:

CREATE OR REPLACE FUNCTION f_near(_middlesystemname text, _jumpsaway int)
  RETURNS TABLE(fromid int, toid int, jumps int, name text, security text) AS
$func$
DECLARE
    _jumps integer;
BEGIN

CREATE TEMP TABLE map AS
SELECT -1 AS "fromSolarSystemID"
      ,m."solarSystemID" AS "toSolarSystemID"
      ,0 AS level
FROM   "mapSolarSystems" m
WHERE  "solarSystemName" = _middlesystemname;

-- potentially add indexes on the temp table and ANALYZE if it gets big

FOR _jumps IN 1 .. _jumpsaway LOOP
    INSERT INTO map ("fromSolarSystemID", "toSolarSystemID", level)
    SELECT sj."fromSolarSystemID", sj."toSolarSystemID", _jumps AS level
    FROM   "mapSolarSystemJumps" sj
    JOIN   map m ON  m."toSolarSystemID" = sj."fromSolarSystemID"
                 AND m."level" = _jumps - 1
    LEFT   JOIN map mx ON mx."fromSolarSystemID" = sj."fromSolarSystemID"
    WHERE  mx."fromSolarSystemID" IS NULL;
END LOOP;

RETURN QUERY
SELECT m.*, s."solarSystemName", s."security"
FROM   map m
JOIN   "mapSolarSystems" s ON m."toSolarSystemID" = s."solarSystemID";

END
$func$ LANGUAGE plpgsql;

RECURSIVE CTE - doesn't seem to work

This short SQL query with a recursive CTE should have done it:

WITH RECURSIVE map AS (
    SELECT -1 AS fromsolarsystemid, m.solarsystemid, 0 AS level
    FROM   mapsolarsystems m
    WHERE  m.solarsystemname = from_id

    UNION  ALL
    SELECT sj.fromsolarsystemid, sj.tosolarsystemid, level + 1
    FROM   mapsolarsystemjumps sj 
    JOIN   map m USING (level)
    LEFT   JOIN map mx USING (fromsolarsystemid)
    WHERE  sj.fromsolarsystemid = m.tosolarsystemid
    AND    mx.fromsolarsystemid IS NULL
    AND    m.level < 10                      -- jumpsAway
    )
SELECT m.*, s.solarsystemname, s.security
FROM   map m
JOIN   mapsolarsystems s ON m.tosolarsystemid = s.solarsystemid
-- WHERE s.security < 0.45  -- uncomment to check all nearby lowsec system 

However:

ERROR:  recursive reference to query "map" must not appear within an outer join
LINE 9:     LEFT   JOIN map mx USING (fromsolarsystemid)
Community
  • 1
  • 1
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • That doesn't work because I don't have a table called map, should I just make one? – mtfurlan Jun 10 '13 at 23:58
  • @Scuzzball: You don't need a table called map for this. It's just the name of the working table for the CTE. All you need is mapsolarsystems and mapsolarsystemjumps. **However**, testing this I found that we can't have a LEFT JOIN to the working table in the recursive term. So my solution is not going to fly. I'll provide a plpgsql solution ... – Erwin Brandstetter Jun 11 '13 at 00:12
  • Do you want me to get you the data I'm running this on? – mtfurlan Jun 11 '13 at 00:16
  • @A small data sample to test code is *always* a good idea (in your question). Without it, I am writing blindly. But I'll have a shot in the dark ready soon. – Erwin Brandstetter Jun 11 '13 at 00:24
  • The results of `pg_dump -t 'map*' evesd > mapStuff` is at [http://uf.serveftp.com/files/mapStuff](http://uf.serveftp.com/files/mapStuff) It seems to set me as the user and stuff, sorry about that. – mtfurlan Jun 11 '13 at 00:33
  • @Scuzzball: I think the word "small" got lost in translation. :) I am not going to deal with this huge dump. Either way, I added a plpgsql version, untested. – Erwin Brandstetter Jun 11 '13 at 00:37
  • Sorry, I just have no idea how to make it smaller. Testing now. – mtfurlan Jun 11 '13 at 00:40
  • Okay, I made a few changes, but I don't really know how joins work, and can't debug that. http://pastebin.com/jSELe4sT The data is case sensitive, and it's annoying. Also, if yo utell me how, I could give you some example data, I just don't know how to limit it. – mtfurlan Jun 11 '13 at 01:13
  • @Scuzzball: CaMeL case names are tedious in Postgres. Anyway, adopted them and fixed the error that snuck into the `LEFT JOIN`. Give it another shot. – Erwin Brandstetter Jun 11 '13 at 02:08
  • Okay, it works now, with a few modifications on the return data types, which was my fault for not having sample data. Sorry about that, and thanks. Final code is http://pastebin.com/Y3vyXm2N – mtfurlan Jun 11 '13 at 02:31