How to model events with locations that have a label and lat, lng coordinates? - sql

Assume a special event is happening at this location:
{
label: 'Mountain View',
lat: 37.4224764,
lng: -122.0842499
}
How would you store this information and why?
1) Single table
events: id place_label coordinates(point)
2) Two separate tables
events: id place_id
places: place_id label coordinates(point)
3) Single table using jsonb data type
events: id place(jsonb)
or another way?
The end goal would be to use geospatial extensions (e.g. EarthDistance, PostGIS) to do queries like "find all events within ___ miles".

Do two separate tables otherwise you suffer the disadvantages of querying a larger table for point-in-polygon than needed.
CREATE TABLE places
(
id_place serial PRIMARY KEY,
name text,
geom geography(POINT)
);
CREATE INDEX ON places USING gist(geom);
CREATE TABLE events
(
id_event serial PRIMARY KEY,
id_place int REFERENCES place,
start_ts timestamp
);
The end goal would be to use geospatial extensions (e.g. EarthDistance, PostGIS) to do queries like "find all events within ___ miles".
SELECT *
FROM events AS e
JOIN places AS p USING (id_place)
WHERE ST_DWithin(p.geom, ST_MakePoint(long,lat)::geography, miles*1609);
That's all events within miles of (long,lat). I wouldn't use earthdistance, I would just use PostGIS.

Related

SQLSERVER - Calculate nodes loops in Network Chart

I have a table that contains all the "Edges" between "Nodes" with relation "from node to node".
Table Nodes looks like this:
CREATE TABLE Nodes(
Id VARCHAR(10) PRIMARY KEY,
Name VARCHAR(50)
)
CREATE TABLE Edges(
Id VARCHAR(100) PRIMARY KEY,
FromId VARCHAR(10),
ToId VARCHAR(10)
)
These tables are used to describe a network chart like the one rapresented in the image.
I have to create a recursive query in order to get all nodes the paths that pass throug "FROM" nodes
and and arrive to "TO" nodes.
I have also to avoid infinite loop like the one highlighted in yellow rectangle.
So basically the spider have to check for all the nodes the available paths, track the nodes that
it has already passed and then go ahead till It reach "TO" nodes.
Then in a second moment, once I have all the routes, I need to test what are the routes thas has
arrived to destination passing throug the FROM path.
How can i do this by Query ?
Thanks to support

How to insert data into a relation table correctly in SQL?

I have some data in a general table called ImportH. The data has been imposted from a csv file. I have also created two tables, Media and Host (each one has it's respective ID. These tables are related by a third table called HostMedia.
Each Host can have (or not) different types of Media (facebook, email, phone...).
I'll provide some images of the tables:
Table ImportH
Table Host
Table Media
How can I insert the data from the other tables into table HostMedia? This table looks like this:
create table HostMedia (
host_id int references Host (host_id),
id_media int references Media (id_verification),
primary key (host_id, id_media)
);
I have tried this:
insert into HostMedia (host_id, id_media)
select Host.host_id, Media.id_verification
from Host, Media;
But this does the cartesian product for all the hosts assigning them all the rows on the Media table. What's the correct way?
The "media" column in your "ImportH" table looks almost like a valid JSON, so this might work:
INSERT INTO HostMedia (host_id, id_media)
SELECT i.host_id, m.id_verification
FROM (
SELECT host_id,
json_array_elements_text(replace(media,'''','"')::json) AS media_name
FROM ImportH
) AS i
JOIN Media AS m ON m.media = i.media_name;
Notes: it would be easier if you
provided text data instead of screenshots
used logical column names

Many to many relationship when order metter

I have many to many relation between User and Group: group may contains few users and consists in multiply groups.
Server send me json like this, and I need to store it to DB:
group: {
name: "test", id: "UUID"
users: [
{name: "user1", id: "UUID"},
{name: "user2", id: "UUID"},
{name: "user3", id: "UUID"}
]
}
The problem is: users order in group matters - users should be displayed in the same order like in servers json.
The fist solution that I use is add to group field like
user_ids: [user1_uuid, user2_uuid, user3_uuid] and store it into DB in json fromat, but I see 2 problems in this solution:
It contradicts to first normal form (1NF)
When I SELECT values they have wrong order:
SELECT * FROM user WHERE id IN (:user_ids)
How to store it into DB and SELECT?
In SQL, table rows indeed are unordered; the only guaranteed way to get ordered rows is to use ORDER BY.
If the order of your array elements is important, add a column for the array index.
You could use the following schema:
CREATE TABLE user(id integer primary key, name text);
CREATE TABLE grp(id integer primary key, name text);
CREATE TABLE usergrp(userid integer, grpid integer, position integer);
CREATE INDEX positionindex on usergrp(grpid, position);
Then you can retrieve users belonging to a group with the query
SELECT userid FROM usergrp WHERE grpid=? ORDER BY position;
Also, maybe you would like to use the "without rowid" sqlite table storage option for usergrp.

Is it possible to CREATE TABLE with a column that is a combination of other columns in the same table?

I know that the question is very long and I understand if someone doesn't have the time to read it all, but I really wish there is a way to do this.
I am writing a program that will read the database schema from the database catalog tables and automatically build a basic application with the information extracted from the system catalogs.
Many tables in the database can be just a list of items of the form
CREATE TABLE tablename (id INTEGER PRIMARY KEY, description VARCHAR NOT NULL);
so when a table has a column that references the id of tablename I just resolve the descriptions by querying it from the tablename table, and I display a list in a combo box with the available options.
There are some tables however that cannot directly have a description column, because their description would be a combination of other columns, lets take as an example the most important of those tables in my first application
CREATE TABLE bankaccount (
bankid INTEGER NOT NULL REFERENCES bank,
officeid INTEGER NOT NULL REFERENCES bankoffice,
crc INTEGER NOT NULL,
number BIGINT NOT NULL
);
this as many would know, would be the full account number for a bank account, in my country it's composed as follows
[XXXX][XXXX][XX][XXXXXXXXXX]
^ ^ ^ ^
bank id | crc account number
|
|_ bank office id
so that's the reason of the way my bankaccount table is structured as is.
Now, I would like to have the complete bank account number in a description column so I can display it in the application without giving a special treatment to this situation, since there are some other tables with similar situation, something like
CREATE TABLE bankaccount (
bankid INTEGER NOT NULL REFERENCES bank,
officeid INTEGER NOT NULL REFERENCES bankoffice,
crc INTEGER NOT NULL,
number BIGINT NOT NULL,
description VARCHAR DEFAULT bankid || '-' || officeid || '-' || crc || '-' || number
);
Which of course doesn't work since the following error is raised1
ERROR: cannot use column references in default expression
If there is any different approach that someone can suggest, please feel free to suggest it as an answer.
1 This is the error message given by PostgreSQL.
What you want is to create a view on your table. I'm more familiar with MySQL and SQLite, so excuse the differences. But basically, if you have table 'AccountInfo' you can have a view 'AccountInfoView' which is sort of like a 'stored query' but can be used like a table. You would create it with something like
CREATE VIEW AccountInfoView AS
SELECT *, CONCATENATE(bankid,officeid,crc,number) AS FullAccountNumber
FROM AccountInfo
Another approach is to have an actual FullAccountNumber column in your original table, and create a trigger that sets it any time an insert or update is performed on your table. This is usually less efficient though, as it duplicates storage and takes the performance hit when data are written instead of retrieved. Sometimes that approach can make sense, though.
What actually works, and I believe it's a very elegant solution is to use a function like this one
CREATE FUNCTION description(bankaccount) RETURNS VARCHAR AS $$
SELECT
CONCAT(bankid, '-', officeid, '-', crc, '-', number)
FROM
bankaccount this
WHERE
$1.bankid = this.bankid AND
$1.officeid = this.officeid AND
$1.crc = this.crc AND
$1.number = this.number
$$ LANGUAGE SQL STABLE;
which would then be used like this
SELECT bankaccount.description FROM bankaccount;
and hence, my goal is achieved.
Note: this solution works with PostgreSQL only AFAIK.

Database design for a template based evaluation system

We are working on a database to store some evaluations we conduct. There are a few different types of evaluations and some have changed over time. Because of this we need to keep a record of exactly what an evaluation looked like when it was undertaken.
I figured that the best way to support this would be through a template style system.
With:
A table saving all possible options;
A table mapping options to a template;
An evaluations table mapping a participant to a template on a date/time; and
A table mapping evaluator comments to an option of an evaluation.
This is a skeleton for the design:
CREATE TABLE options (
id SERIAL PRIMARY KEY,
option TEXT NOT NULL
);
CREATE TABLE templates (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE template_options (
template INTEGER NOT NULL REFERENCES templates( id ),
option INTEGER NOT NULL REFERENCES options( id ),
UNIQUE ( template, option )
);
CREATE TABLE participants (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE evaluations (
id SERIAL PRIMARY KEY,
template INTEGER NOT NULL REFERENCES templates( id ),
participant INTEGER NOT NULL REFERENCES participants( id ),
date TIMESTAMP WITH TIME ZONE NOT NULL
);
CREATE TABLE evaluation_data (
template INTEGER NOT NULL REFERENCES templates( id ),
option INTEGER NOT NULL REFERENCES options( id ),
evaluator_comments TEXT NOT NULL,
);
The design is able to capture our data but doesn't restrict the options saved in evaluation_data to the subset specified in the evaluation's template's option mapping. We could probably enforce it with a trigger (we can definitely do it with application logic [we are doing so at the moment]) but are we going down the wrong path with this design?
Can anybody think of a better way to do it?
Edit:
Added an example of a potential trigger we would need to use to ensure valid options are enforced with this design.
CREATE FUNCTION valid_option() RETURNS trigger as $valid_option$
BEGIN
IF NOT NEW.option IN ( SELECT template_options.option
FROM template_options
INNER JOIN templates
ON template_options.template = templates.id
WHERE templates.id = ( SELECT evaluations.template
FROM evaluations
WHERE evaluations.id = NEW.evaluation ) ) THEN
RAISE EXCEPTION 'This option is not mapped for this evaluations template.';
END IF;
RETURN NEW;
END
$valid_option$ LANGUAGE plpgsql;
CREATE TRIGGER valid_option BEFORE INSERT ON evaluation_data FOR EACH ROW EXECUTE PROCEDURE valid_option();
Remember that you need two sets of tables. The first set containing the assessment, questions, answer alternatives, categories(?) needed to display the assessment to the participant. The second set of tables to record data about the evaluation (ie. the participant taking the assessment): which assessment, which questions, which answer alternatives and in which order they were presented, which answer they entered (are they allowed to answer the same question multiple times?), etc.
We're using the following structure (I've removed topic scoring since you didn't ask about it):
Models for presenting an assessment:
Assessment: assessment_name, passing_status, version
Question: assessment, question_number, question_type, question_text
AnswerAlternative: question, correct?, answer_text, points
Models for recording an evaluation (participant taking an assessment):
Progress: started_timestamp, finished_timestamp, last_activity, status (includes "finished")
Result: user, assessment, progress, currently_active, score, passing_grade?
Answer: result, question, selected_answer_alternative, answer_text, score
To achieve your goal, I would augment this by writing the generated evaluation to a table and pointing to it from Reault. You could also record the selection and presentation criteria so you can re-generate the assessment programmatically (ie. if you're selecting the questions from a larger question db and re-ordering the answer alternatives before presenting them to the participant).