Self join and recursive selection in a table - mysql

Assuming a table as below
| ID | NAME | ROLE | MGRID |
---------------------------
| 1 | ONE | 5 | 5 |
| 2 | TWO | 5 | 5 |
| 3 | THREE | 5 | 6 |
| 4 | FOUR | 5 | 6 |
| 5 | FIVE | 15 | 7 |
| 6 | SIX | 25 | 8 |
| 7 | SEVEN | 25 | 7 |
| 8 | EIGHT | 5 | 8 |
How do I get a list of all employees reporting to an employee, including the ones who are in subsequent reporting levels below?
I mean, given emp id 5, I should get [1, 2] and given 7, I should get [1, 2, 5, 7]. How do I get this done?
Will self joins be of help here? Need to brush up my knowledge on joins now.

SELECT id
FROM emp
START WITH id = 7
CONNECT BY NOCYCLE mgrid = PRIOR id
SQLFIDDLE LINK

Here is a SQL statement using Oracle.
select id, name, role, mgrID
from employees
start with id = 7
connect by NoCycle prior id = mgrid;
Please note that the manager for employee 7 is the employee 7 - they are their own manager. This will cause an error - "Connect By loop in user data'. By using the NoCycle keyword you can tell Oracle to detect this and avoid the error.
Does this solve your issue?

I know this isn't exactly what you were asking, but if you are willing to choose a finite number of level's to recurse it isn't too bad to write.
SELECT table_2.id
FROM table LEFT JOIN
(table AS table_1 LEFT JOIN table AS table_2 ON table_1.id = table_2.MgrID)
ON table.id = table_1.MgrID
WHERE (((table.id)=7));
ETC.

Related

Get one row in table1 and Two or more rows in table2 in one array

Im newbie here. Suppose 2 tables in database:
Table 1: tournaments
+------------+----------+---------+----------+
| id | format | Size | host |
+------------+----------+---------+----------+
| 7 | Single | 9 | Daniel |
| 8 | Single | 4 | Oscar |
+------------+----------+---------+----------+
Table 2: matchs
+------------+----------+-------------+----------+----------+
| id | player | position | score | winner |
+------------+----------+-------------+----------+----------+
| 7 | Arturo | 0 | 3 | 1 |
| 7 | Pablo | 1 | 2 | 0 |
| 8 | Ale | 0 | 1 | 0 |
| 8 | Maria | 1 | 5 | 1 |
+------------+----------+-------------+----------+----------+
Now i want to get the host, size, winner, player, Score where ID is 7 and 8. Do you know how iget the following structure?:
[ Host:Daniel, size: 9, [ winner:1, player: Arturo, Score: 3], [winner:0, player: Pablo, Score: 2]], [host:Óscar, size:4,[winner:0, player: Ale, Score:0],....
Thanks in advance :)
Use JOIN Syntax to join the tables by id field:
SELECT
host, size, winner, player, score
FROM
tournaments INNER JOIN matchs USING (id);
If you want to select rows that contains specific idonly, then use WHERE clause. In your case append following to the query before semicolon:
WHERE `id` IN (7, 8)
Note, the MySQL can not create complex objects. SELECT returns set of rows only. To convert returned data set to structure, use external tools of your server side platform: C, C++, C#, VB.NET, Python, PHP, etc.

How to properly join two tables to use alternative ORDER BY

Two tables...
people (personid, name, mainordering)
entries (userid, personid, altordering)
"personid" is the common field. My app displays a draggable list users can move around. When done, they click to "lock" in their order.
Table : people
+----------+---------+--------------+
| personid | name | mainordering |
+----------+---------+--------------+
| 1 | Bob | 2 |
| 2 | Charlie | 4 |
| 3 | Jim | 1 |
| 4 | Doug | 3 |
+----------+---------+--------------+
So using mainordering, it would display:
Jim
Bob
Doug
Charlie
entries table might have (for user 16):
+--------+----------+-------------+
| userid | personid | altordering |
+--------+----------+-------------+
| 16 | 1 | 3 |
| 16 | 2 | 1 |
| 16 | 3 | 2 |
| 16 | 4 | 4 |
+--------+----------+-------------+
So if user 16 has already submitted his entry BUT NOT LOCKED IT IN, I want to display his list using altordering. i.e.
Charlie
Jim
Bob
Doug
I'm struggling with the proper join to use. Here is what I tried and isn't working (it's simply ordering by mainordering still)...
$sql = "SELECT * from entries
WHERE userid=".$_SESSION['userid']."
LEFT JOIN people ON entries.personid = people.personid
ORDER BY altordering";
Any thoughts would be much appreciated. Thank you...
Are you sure you don't get an error when using WHERE before JOIN?
It should work like this:
SELECT people.*
FROM people
JOIN entries ON entries.personid = people.personid
WHERE entries.userid={$_SESSION['userid']}
ORDER BY entries.altordering
I assume entries.personid will always have a matching person in people, so you should use an INNER JOIN. You would use FROM entries LEFT JOIN people if you wanted to retrieve altordering even for non-existing people.

SQL join several tables based on latest entry in transaction table per join record

I have a transaction table with timestamps
a transaction has one event and one user.
All transactions have an event,
All events have at least one trasaction,
Each transaction has a user that must exist,
A User will not necessarily have a transaction.
The output will be a sort of the evt list
Output line count should equal db.evt record count.
The first column of each table is the Autoinc unique index.
In transaction, these are fks to the other tables.
The problem is that I need the transaction with the latest timestamp for the evt in the transaction table.
I am still relatively new to SQL (Using MySQL) and while I muddle through joins. I have no idea how to get the latest record by evID by timestamp.
I have looked at other questions on the topic but not found one that addresses mine. (Granted there are 14K on Joins alone, so I may have missed one)
Sample Table Data below:
Table structure is hopefully obvious by I will edit it in if requested.
Edit:
I've changed the names of tables and columns for clarity (and to avoid matching keywords)
I tried Stuart's answer below and got an error:
Answer:
SELECT
eventTable.EvtName AS EvtD,
transTable.TranAct AS LastTrans,
userTable.UserName AS UsrNm
FROM
transTables,
INNER JOIN (
SELECT evtID, MAX(TransID) TransID FROM transTable GROUP BY evtID
) last ON last.evtID = transTable.evtID AND last.TransID = transTable.TransID
INNER JOIN eventTable ON eventTable.evtID = transTable.evtID
INNER JOIN userTable ON userTable.usId = transTable.usId
Response:
#1064 - You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the right syntax to use near
'INNER JOIN (
SELECT evtID, MAX(TransID) TransID FROM transTable GROUP BY evt'
at line 7
Tables:
db.transTable
| TransID | EvtID | TranAct | timestamp | UserID
----------------------------------------------------------------
| 1 | 1 | add | 2014-05-08 08:10:00.000 | 3
| 2 | 2 | add | 2014-05-08 09:10:00.000 | 2
| 3 | 3 | add | 2014-05-08 10:10:00.000 | 3
| 4 | 2 | validate | 2014-05-08 11:10:00.000 | 5
| 5 | 3 | validate | 2014-05-08 12:10:00.000 | 3
| 6 | 2 | reverse | 2014-05-08 13:10:00.000 | 1
| 7 | 1 | edit | 2014-05-08 14:10:00.000 | 4
| 8 | 4 | add | 2014-05-08 15:10:00.000 | 3
| 9 | 5 | add | 2014-05-08 16:10:00.000 | 2
db.eventTable
| EvtID | EvtName
-----------------
| 1 | Evt1
| 2 | Evt2
| 3 | Evt3
| 4 | Evt4
| 5 | Evt5
db.userTable
| UserID | UserName
--------------------
| 1 | Usr1
| 2 | Usr2
| 3 | Usr3
| 4 | Usr4
| 5 | Usr5
Desired output:
eventTable.EvtName AS EvtD
transTable.TranAct AS LastTrans
userTable.UserName AS UsrNm
| EvtD | LastTrans | UsrNm
--------------------------
| Evt1 | edit | Usr4
| Evt2 | reverse | Usr1
| Evt3 | validate | Usr3
| Evt4 | add | Usr3
| Evt5 | add | Usr2
Much thanks for any assistance.
Something like this shuold work where a derived table is used to eliminate all transactions except the latest per evId.
SELECT
eventTable.EvtName AS EvtD,
transTable.TranAct AS LastTrans,
userTable.UserName AS UsrNm,
FROM
transTable
INNER JOIN (
SELECT evId, MAX(UID) uid FROM transTable GROUP BY evId
) last ON last.evId = transTable.evId AND last.uid = transTable.uid
INNER JOIN eventTable ON eventTable.evId = transTable.evId
INNER JOIN userTable ON userTable.usId = transTable.usId

Joining from another table multiple times in a MySQL query

I am trying to do multiple joins on the same MySQL table, but am not getting the results that I expect to get. Hopefully someone can point out my mistake(s).
Table 1 - cpe Table
|id | name
|----------
| 1 | cat
| 2 | dog
| 3 | mouse
| 4 | snake
-----------
Table 2 - AutoSelect
|id | name | cpe1_id | cpe2_id | cpe3_id |
|-----------------------------------------------
| 1 | user1 | 1 | 3 | 4 |
| 2 | user2 | 3 | 1 | 2 |
| 3 | user3 | 3 | 3 | 2 |
| 4 | user4 | 4 | 2 | 1 |
------------------------------------------------
I would like to see an output of
user1 | cat | mouse | snake |
user2 | mouse | snake | dog |
..etc
Here is what I have tried
SELECT * FROM AutoSelect
LEFT JOIN cpe ON
( cpe.id = AutoSelect.cpe1_id ) AND
( cpe.id = AutoSelect.cpe2_id ) AND
( cpe.id = AutoSelect.cpe3_id )
I get blank results. I thought i knew how to do these joins, but apparently when I'm trying to match cpe?_id with the name of the cpe table.
Thanks in advance for any assistance.
You need left join 3 times as well. Currently your query only joins 1 time with 3 critieria as to the join. This should do:
SELECT a.name, cpe1.name, cpe2.name, cpe3.name FROM AutoSelect as a
LEFT JOIN cpe as cpe1 ON ( cpe1.id = a.cpe1_id )
LEFT JOIN cpe as cpe2 ON ( cpe2.id = a.cpe2_id )
LEFT JOIN cpe as cpe3 ON ( cpe3.id = a.cpe3_id )
And you probably mean to INNER JOIN rather than LEFT JOIN unless NULL values are allowed in your AutoSelect table.
I think your design is wrong.
With tables like that, you get it the way it's meant to be in relational databases :
table 1 : animal
id name
1 cat
2 dog
3 mouse
4 snake
table 2 : user
|id | name |
|--------------
| 1 | user1 |
| 2 | user2 |
| 3 | user3 |
| 4 | user4 |
table 3 : association
|id_user | id_animal|
|--------------------
| 1 | 1 |
| 1 | 3 |
| 1 | 4 |
| 2 | 3 |
| 2 | 1 |
| 2 | 2 |
| 3 | 3 |
| 3 | 2
| 4 | 4 |
| 4 | 2 |
| 4 | 1 |
---------------------
Then :
select u.name, a.name from user u, animal a, association ass where ass.user_id = u.id and ass.animal_id = a.id;
In this case, your solution won't produce a good dynamic database. There are other ways to make combinations of multiple tables. I can show you by my own database what you should use and when you should use this solution. The scheme is in dutch, but you'll probably understand the keywords.
Like you, I had to combine my windmills with a kWh-meter, which has to measure the energyproduction of my windmills. What you should do, is this case, is making another table(in my case molenkWhlink). Make sure your tables are INNODB-types(for making Foreign keys). What I've done is combining my meters and mills by putting a pointer(a foreign key) of their ID(in Dutch Volgnummer) in the new table. An advantage you may not need, but I certainly did, is the fact I was able to extend the extra table with connection and disconnection info like Timestamps and metervalues when linking or unlinking. This makes your database way more dynamic.
In my case, I Also had a table for meassurements(metingoverzicht). As you can see in the scheme, I've got 2 lines going from Metingoverzicht to molenkwhlink. The reason for this is quite simple. All meassurements I take, will be saved in table Metingoverzicht. Daily meassurements(which are scheduled) will have a special boolean put on, but unscheduled meassurements, will also me saved here, with the bollean turned off. When switching meters, I need the endvalue from the leaving meter and the startvalue from the new meter, to calculate the value of todays eneryproduction. This is where your solution comes in and an extra table won't work. Usually, when you need just one value from another table a JOIN will be used. The problem in this case is, I've got 2 meassurementIDs in 1 link(1 for connecting and 1 for disconnecting). They both point to the same tablecolumn, because they both need to hold the same type of information. That is when you can use a double JOIN from one table towards the other. Because, both values will only be used once, and just needed to be saved in a different place to avoid having 1 action stored on different locations, which should always be avoided.
http://s1101.photobucket.com/user/Manuel_Barcelona/media/schemedatabase.jpg.html

Complicated MySQL Data Structure/Manipulation Problem

First off, I apologize for the length. This is kind of complicated (at least for me).
Background on the database:
I have a products, variables, and prices table. "Products" are the main information regarding a product (description, title, etc). "Prices" have information about each price (price, cost, minimum qty required, shipping cost, etc), as some products can have more than one price (a 10" widget is a different price than a 12" widget, for instance). "Variables" are variations to the product that do not change the price, such as color, size, etc.
Initially (when I built this database about 7 years ago) I had the variable information stored in the first price in a list of prices for the same product in a pipe-delimited format (yes, I know, badbadbad). This worked in general, but we've always had a problem, though, where sometimes a variable wouldn't be consistent among all the prices.
For instance, a Widget (product) may be 10" or 12" and sell for $10 and $20 (prices) respectively. However, while the 10" widget may be available in blue and red (variables), the 12" widget is only available in red. We ameliorated this problem by adding a little parenthetical statement in the incongruent variable like "Red (10" ONLY)". This sort of works, but customers are not always that smart and a lot of time is devoted to fixing mistakes when a customer selects a 12" widget in red.
I have since been tasked with modernizing the database and have decided to put the variables in their own table and making them more dynamic and easier to match with certain prices, as well as keep a more dummy-proof inventory (you can't imagine the nightmares).
My first step was to write a stored procedure on my test db (for when I do the conversion) to process all the existing variables into a new variable table (and label table, but that's not really important, I don't think). I effectively parsed out the variables and listed them with the correct product id and the product id they were initially associated with in the variable table. However, I realized this is only a part of the problem, since I (at least for the initial transformation of the database) want each variable to be listed as being connected to each price for a given product.
To do this, I created another table, like so:
tblvariablesprices
variablepriceid | variableid | priceid | productid
which is a many-to-many with the variable table.
Problems:
My problem now is, I don't know how to create the rows. I can create a left join on my prices and variables tables to get (I think) all the necessary data, I just don't know how to go through it. My sql is (mysql 5.0):
SELECT p.priceid, p.productid, variableid, labelid
FROM tblprices p
LEFT JOIN tblvariables v ON p.priceid = v.priceid
ORDER BY productid, priceid
This will get me every priceid and productid and any matching variable and label ids. This is good in certain instances, such as when I have something like:
priceid | productid | variableid | labelid
2 | 7 | 10 | 4
2 | 7 | 11 | 4
2 | 7 | 12 | 4
3 | 7 | (null) | (null) --- another price for product
because now I know that I need to create a record for priceid 2 and variableids 10, 11, 12, and then also for priceid 3 for that product. However, I also get results from this dataset for products with no variables, products with one price and multiple variables, and products with multiple prices and no variables, for instance:
priceid | productid | variableid | labelid
2 | 7 | 10 | 4
2 | 7 | 11 | 4
2 | 7 | 12 | 4
3 | 7 | (null) | (null)
4 | 8 | (null) | (null) --- 1 price no variables
5 | 9 | 13 | 5 --- mult vars, 1 price
5 | 9 | 14 | 5
5 | 9 | 15 | 6
5 | 9 | 16 | 6
6 | 10 | (null) | (null) --- mult price, no vars
7 | 10 | (null) | (null)
8 | 10 | (null) | (null)
Taking the above dataset, I want to add entries into my tblpricesvariables table like so:
variablepriceid | variableid | priceid | productid
1 | 10 | 2 | 7
2 | 11 | 2 | 7
3 | 12 | 2 | 7
4 | 10 | 3 | 7
5 | 11 | 3 | 7
6 | 12 | 3 | 7
7 | 13 | 5 | 9
8 | 14 | 5 | 9
9 | 15 | 5 | 9
10 | 16 | 5 | 9
I have thousands of records to process, so obviously doing this manually is not the answer. Can anyone at least point me in the correct direction, if not come up with a sproc that could handle this type of operation? I also would welcome any comments on how to better organize and/or structure this data.
Thank you so much for reading all this and helping me out.
How about:
SELECT DISTINCT b.variableid, a.priceid, a.productid
FROM tblprices AS a
JOIN tblprices AS b ON a.productid = b.productid
WHERE b.labelid IS NOT NULL
ORDER BY priceid;
+------------+---------+-----------+
| variableid | priceid | productid |
+------------+---------+-----------+
| 10 | 2 | 7 |
| 11 | 2 | 7 |
| 12 | 2 | 7 |
| 10 | 3 | 7 |
| 11 | 3 | 7 |
| 12 | 3 | 7 |
| 13 | 5 | 9 |
| 14 | 5 | 9 |
| 15 | 5 | 9 |
| 16 | 5 | 9 |
+------------+---------+-----------+
INSERTing into tblvariables is left as an exercise for the reader ;)
I think this should work:
SELECT v.variableid, p.productid, p.priceid
FROM tblvariables v, tblprices p
WHERE v.priceid IN (SELECT s.priceid
FROM tblprices s
WHERE s.productid = p.productid);
Next time, can you throw in create and insert statements to replicate your setup? Thanks.