Create loop within SQL SELECT statement until chain broken - mysql

Hypothetical database for events happening around the world.
EVENT
event_id | event_name
1 | Great Wall Party
2 | Times Square Dance
3 | Sydney Blowout
PLACE
place_id | place_name
54 | Times Square
55 | Manhattan
56 | New York City
57 | New York State
58 | USA
EVENTPLACE
eventid | placeid
2 | 54
RELATEDPLACES
rel_placeid1 | rel_placeid2
54 | 55
55 | 56
56 | 57
57 | 58
If I display the event, Times Square Dance, I’d like to display all the places that appear up the chain of its associated places via the RELATEDPLACES table (i.e. Times Square, Manhattan, New York City, New York State, USA). Likewise, if I call all events for USA, I’d like the Times Square dance to be listed, given its EVENTPLACE (Times Square) appears at the bottom of the RELATEDPLACES chain of associations starting with USA.
I think I need to create an inner loop within my SQL command so that it keeps performing until there is a break in the chain. So far (using the first of the two above examples) I have:-
SELECT place_nm FROM eventplace
INNER JOIN relatedplaces ON placeid = rel_placeid1
INNER JOIN place ON rel_placeid2 = place_id
[where the loop should begin:
INNER JOIN relatedplaces ON place_id = rel_placeid1
INNER JOIN place ON rel_placeid2 = place_id
end loop]
WHERE eventid = ‘2’;
This is complicated by the fact that I need different table aliases for each loop, which means I can’t state in the opening SELECT statement that I want to be collecting all the place_name data in the same column.
I’m not sure if I what I am trying to achieve is even possible and my current fallback solution is to list all of Times Square’s related places in the RELATEDPLACES table, rather than just the next largest place (Manhattan), but this seemed like the better solution (and would also save database space).
Can anyone suggest the SQL SELECT command I might need to use? Cheers!

Quite an intriguing problem. But the issue is that MySQL currently does not allow for recursive queries. You have two options :
Decided on the level of RelatedEvents upto which you want to dig into, and create a query for the same (using excel or a small C code)
Use a program to generate and execute the queries on the fly; recursively make DB queries from the program. I suggest this is the best option, though requires repeated DB access.

Related

How to separate one column's data into multiple columns?

Here's my situation : I have a table that has large amounts of records, I need to pull out a number of these records for each name in the database, note that TOP will not work for my use case. My end user wants the report formatted in such a way that each user shows up only once, and up to 3 different dates are shown for the user.
Table format
AutoID
Enum
TNum
Date
Comments
1
25
18
2/2/22
2
25
18
1/2/21
Blah
3
18
18
1/2/21
4
18
18
1/2/20
5
25
17
1/2/22
6
25
17
1/2/20
Now the Enum and TNum fields are fk with other tables, I have created a join that pulls the correct information from the other tables. In the end my query provides this output
RecordID
Training
CompletedDate
FirstName
LastName
Location
2821
MaP
1/1/21
David
Simpson
123 Sesame St.
2822
1/2/22
Fuller
MaP
Dough
GHI
David
123 Sesame St.
2825
1/1/20
Simpson
The two "Blank fields" represent information that is pulled and may or may not be needed in some future report.
So to my question : How do I manage to get a report, with this query's pull to look like this:
Place
LastName
FirstName
Training
FirstCuttoff
Secondcutoff
ThirdCutoff
Comments
123 Sesame St.
David
Simpson
MaP
1/1/20
1/1/21
123 Sesame St.
John
Dough
MaP
1/1/22
I was originally planning on joining my query to itself using where clauses. But when I tried that it just added two extra columns of the same date. In addition it is possible that each record is not identical; locations may be different but since the report needs the most recent location and the name of the trainee. In addition, to add more complexity, there are a number of people in the company with effectively the same name as far as the database is concerned, so rejoining on the name is out. I did pull the Enum in my query, I can join on that if needed.
Is there an easier way to do this, or do I need to sort out a multiple self-joining query?
I have a project I am working on where I am going to have to do this. Some of the suggestions I received were to use a Pivot query. It wouldn't work in my case but it might for yours. Here is a good example
Pivot Columns

Displaying Multiple Column Values for One Row Value

I currently have a database which houses county codes within a state and clients doing business within those counties. Sometimes, several clients will operate within the same counties. I am looking to display each county code and then that county codes associated clients as separate columns. Example would look something like the below:
County Code Client1 Client2 Client3
32 1 2
42 3
43 6 8
44 2 8 5
45 2
As of now, all I have managed to do is display it as two columns with duplicate county codes displaying different lender IDs. However, this is very manual to put it into the above format once I get it into Excel.
Any suggestions on this?
After review, this can be done with a GROUP_CONCAT(). It puts it as a string so modification with Excel will be needed, but it's a simple solution.
SELECT
COUNTY_CODE,
GROUP_CONCAT(DISTINCT CLIENT_ID)
FROM CLIENT_TABLE
WHERE STATE = 'IA'
GROUP BY COUNTY_CODE
ORDER BY COUNTY_CODE;

MySQL query with 2 JOINs not returning expected result

I have the following 3 tables. This is just a small section of the data, I left out most rows and other columns that I'm not querying against. If it would be helpful to include the full table(s) let me know and I can figure out how to post them.
infocoms
id items_id itemtype value
1735 21 Software 0.0000
1736 22 Software 0.0000
1739 21 Peripheral 151.2500
1741 23 Peripheral 150.5000
1742 24 Peripheral 0.0000
1743 25 Peripheral 0.0000
locations
id name
34 Anaheim
35 Kirkland
36 Palm Springs
37 Tacoma
peripherals
id name locations_id
11 Logitech USB Wheel Mouse 0
12 Samsung Slate Dock 17
21 USB Scan Gun with Stand 34
23 USB Scan Gun with Stand 63
24 USB Scan Gun with Stand 45
26 USB Scan Gun with Stand 39
I am running the following query against these tables:
SELECT peripherals.name, infocoms.value, locations.name AS Branch
FROM peripherals
JOIN infocoms ON peripherals.id = infocoms.items_id
JOIN locations ON peripherals.locations_id = locations.id
WHERE (peripherals.name = 'USB Scan Gun with Stand'
AND peripherals.locations_id != '0')
GROUP BY peripherals.id ORDER BY locations.name ASC
I get the right number of rows returned however the value shows everything as 0.0000 instead of where there are actual amounts (151.25 and 150.50).
Any help or insight would be greatly appreciated. Thanks.
Comment (because I do not have the reputation) : "value" and "name" should be encased in back-ticks (``) because they are reserved words.
But looking at your code a little closer I find that you are grouping by location.name even though many of the values are duplicated when you do an JOIN ON peripherals.locations_id = locations.id.
What happens afterwards is that you GROUP the rest of the statements by location.name giving the first result of all of the locations that do not have a location name associated to the peripherals.locations_id
Try not using GROUPING BY and see what you get. In order to get the results that you want you need to either omit the JOIN ON peripherals.locations_id = locations.id or associate every peripials.locations_id to an appropriate locations.id
#Eugene Scray is right about GROUP BY being the reason why you see only some values.
To keep columns "ungrouped", you need to add those columns into the GROUPING clause. For example:
GROUP BY peripherals.id, infocom.value
In the WHERE clause I added AND infocoms.itemtype = 'Peripheral' which returned the correct number of of rows along with the appropriate value(s).

Order results by proximity (with coordinates & radius)

Given a database of 4 circles, where each circle has a radius and a geolocated centre:
id | radius | latitude | longitude
---+--------+----------+----------
1 | 3 | 40.71 | 100.23
2 | 10 | 50.13 | 100.23
3 | 12 | 39.92 | 100.23
4 | 4 | 80.99 | 100.23
Note: the longitude is the same for each circle, in order to keep things simple.
Assuming that we are on the circle 2, I would like to find every circle nearby, according to the latitude/longitude coordinates and the radius of each circle.
For example, according to the latitude/longitude coordinates, we have this order:
circle 1 (because of proximity: 9.42 <- 50.13 - 40.71)
circle 3 (because of proximity: 10.21 <- 50.13 - 39.92)
circle 4 (because of proximity: 30.86 <- 80.99 - 50.13)
But according to the latitude/longitude coordinates and the radius of each circle, we should have:
circle 3 (because of proximity: 1.79 <- 12 - 10.21)
circle 1 (because of proximity: 6.42 <- 9.42 - 3)
circle 4 (because of proximity: 26.86 <- 30.86 - 4)
Is there a simple way to do so in SQL?
The cube and earthdistance extensions provided in postgresql's contrib can handle doing this, to produce at least approximate answers. Specifically, they assume the Earth is a simple sphere, which makes the math a lot easier.
With those extensions you can produce the distance between circle 2 and the others like this:
select circle.id,
earth_distance(ll_to_earth(circle.latitude, circle.longitude),
ll_to_earth(x.latitude, x.longitude))
from circle,
circle x
where x.id = 2 and circle.id <> x.id
order by 2;
Correcting for the circle radius should just involve subtracting x.radius and circle.radius from the distance above, although you need to think about what units the radius is in. By default, earth_distance will calculate a value in metres.
Now, making the query do something other than scan the entire list of circles and calculate the distance for each one, then sort and limit them, that's much more challenging. There are a couple of approaches:
using cube's ability to be indexed with gist, so you can create indices to search within certain boxes around any circle's centre, and hence cut down the list of circles to consider.
precalculate the distance between each circle and all the others any time a circle is edited, using triggers to maintain this calculation in a separate table.
The second options basically starts with:
create table circle_distance as
select a.id as a_id, b.id as b_id,
earth_distance(ll_to_earth(a.latitude, a.longitude),
ll_to_earth(b.latitude, b.longitude))
from circle a, circle b
where a.id <> b.id;
alter table circle_distance add unique(a_id, b_id);
create index on circle_distance(a_id, earth_distance);
Then some rather tedious functions to delete/insert relevant rows in circle_distance, called by triggers on circle. This means you can do:
select b_id from earth_distance where a_id = $circle_id order by earth_distance limit $n
This query will be able to use that index on (a_id,earth_distance) to do a quick scan.
I'd suggest looking at the PostGIS Geography data types and its associated functions (eg: ST_Distance)rather than reinventing the wheel
In neo4j, you can look at Neo4j Spatial, tests for the different operations at https://github.com/neo4j/spatial/blob/master/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesTest.java, amongst them proximity search, too, e.g. https://github.com/neo4j/spatial/blob/master/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesTest.java#L150
I would souggest you the following:
Create 1 table for calculation of relative distances in relation to the start circle
for instance:
id | calc1 | calc2
---+--------+----------
1 | 9.42 | 1.97
3 | 10.21 | 6.42
4 | 30.86 | 62.86
Calc1 being the calculation without the radius
calc2 being the calculation with radius
then create a store procedure that will first when it is run delete the table and then fill it with the correct data and then just read the result from the destination table
Intrudoction to store procedures
You will allso need cursor for this

saving tree data in database (family tree)

I am trying to store a family tree.
Here is the platform that I am using, Zend framework, Mysql, Ajax
I have searched stackoverflow I came across this post which is very helpful in handling data in terms of objects.
"Family Tree" Data Structure
I'll Illustrate my use case in brief.
User can create family members or friends based on few relations defined in database. I have Model for relations too. User can create family members like Divorced spouse, frineds. Max the Tree can be deep that we are assuming max to kids of the grandchildren but it can expand in width too. Brother/sister & their family.
I am looking an efficient database design for lesser query time. If I have to use the data structures described in above post where I must keep them as they necessary have to be a Model.
For representation I am planning to use Visualization: Organizational Chart from
http://code.google.com/apis/chart/interactive/docs/gallery/orgchart.html#Example
I'll summarize what I need
Database design
Placing of controllers (ajax) & models
The people that the user will create they will not be any other users. just some another data
yeah thats it! I'll post a complete solution on this thread when I'll be completing the project, of course with help of expertise of u guys
Thanks in advance
EDIT I I'll Contribute more to elaborate my situation
I have a user table, a relation table, & last family/family tree table
the Family table must have similar structure to following
ID userid relation id Name
1 34 3 // for son ABC
2 34 4 // for Wife XYZ
3 34 3 // for Mom PQR
4 34 3 // for DAd THE
5 34 3 // for Daughter GHI
6 34 3 // for Brother KLM
The drawback for this approach is generating relations to the other nodes like daughter-in-law, wifes brother & their family.
The ideal way of doing is for a user we can add Parents, siblings, children & for extra relations they must be derived from the family members relation i.e. Brother-in-law must be derived as sister's husband, or wife's brother.
THis is what I can think now. I just need Implementation guidelines.
Hope this helps u guys to provide a better solution.
I guess that from the database point of view it would be best to implement it like
id | name | parent_male | parent_female
Other option would be string prefixing
id | name | prefix
1 | Joe | 0001
2 | Jack | 000100001 //ie. Joes son
3 | Marry| 0001 //ie. Jacks mother
4 | Eve | 0002 // new family tree
5 | Adam | 00020001 // ie. Eves son
6 | Mark | 000200010001 // ie. Adams son
Other (more effective) algorithms like MPTT assume that the data is a tree, which in this case is not (it has circles).
To show it would work - to select Mark's grandparents:
--Mark
SELECT prefix FROM family_tree WHERE id = 6;
-- create substring - trim N 4-character groups from the end where N is N-th parent generation => 2 for grandparent ==> 0002
--grandparents
SELECT * FROM family_tree WHERE prefix = '0002'
-- same for other side of family
-- cousins from one side of family
SELECT * FROM family_tree WHERE prefix LIKE '0002%' AND LENGTH(prefix) = 12