SQL node path reconstruction - mysql

I have a table which contains data about which node has been visited. It is possible that a node can be visited several times. For this I have another table which contains data of the visited node, node visited before and the node visited after. I would now like to reconstruct the path in order of visitation using MySQL. I can't seem to figure out how to make a query for this, so I'm asking here for help.
Example
Let's say someone visited these nodes in this order:
4->5->6->7->4->6->10->12->7->15
The tables would look like this:
Visits
+---------+-------------------------------+----------+------------+
| id | user | node | view_count |
+---------+-------------------------------+----------+------------+
| 1 | l3lie1frl77j135b3fehbjrli5 | 4 | 2 |
+---------+-------------------------------+----------+------------+
| 2 | l3lie1frl77j135b3fehbjrli5 | 5 | 1 |
+---------+-------------------------------+----------+------------+
| 3 | l3lie1frl77j135b3fehbjrli5 | 6 | 2 |
+---------+-------------------------------+----------+------------+
| 4 | l3lie1frl77j135b3fehbjrli5 | 7 | 2 |
+---------+-------------------------------+----------+------------+
| 5 | l3lie1frl77j135b3fehbjrli5 | 10 | 1 |
+---------+-------------------------------+----------+------------+
| 6 | l3lie1frl77j135b3fehbjrli5 | 12 | 1 |
+---------+-------------------------------+----------+------------+
| 7 | l3lie1frl77j135b3fehbjrli5 | 15 | 1 |
+---------+-------------------------------+----------+------------+
Revisits
+---------+-------------------------------+-------+----------------+-----------------+
| id | user | node | after_visiting | before_visiting |
+---------+-------------------------------+-------+----------------+-----------------+
| 1 | l3lie1frl77j135b3fehbjrli5 | 4 | 7 | 6 |
+---------+-------------------------------+-------+----------------+-----------------+
| 2 | l3lie1frl77j135b3fehbjrli5 | 6 | 4 | 10 |
+---------+-------------------------------+-------+----------------+-----------------+
| 3 | l3lie1frl77j135b3fehbjrli5 | 7 | 12 | 15 |
+---------+-------------------------------+-------+----------------+-----------------+
I would like to construct a query that would return the path in the form of a string or a list of nodes like this:
4,5,6,7,4,6,10,12,7,15
or
+---------+--------+
| index | node |
+---------+--------+
| 1 | 4 |
+---------+--------+
| 2 | 5 |
+---------+--------+
| 3 | 6 |
+---------+--------+
| 4 | 7 |
+---------+--------+
| 5 | 4 |
+---------+--------+
| 6 | 6 |
+---------+--------+
| 7 | 10 |
+---------+--------+
| 8 | 12 |
+---------+--------+
| 9 | 7 |
+---------+--------+
| 10 | 15 |
+---------+--------+
Any help will be much appreciated.

change your design to have 1 table visits:
+----+------+------+
| id | user | node |
+----+------+------+
| 1 | xx | 4 |
| 2 | xx | 5 |
| 3 | xx | 6 |
| 4 | xx | 7 |
| 5 | xx | 4 |
| 6 | xx | 6 |
| 7 | xx | 10 |
| 8 | xx | 12 |
| 9 | xx | 7 |
| 10 | xx | 15 |
+----+------+------+
you can then select view_count like this:
select node, count(*) view_count
from visits
where user = :user
group by node
and path like this:
select group_concat(node order by id separator ',') path
from visits
where name = :name

Related

Is there a mySQL procedure that can merge duplicate rows of data into one, then allow me to manipulate that data as if it were one row?

I'm trying to come up with a stored procedure that takes multiple rows that are exactly identical, and combines them into one row while summing one column, which can then be run through more stored procedures based on the sum of that one column.
I've tried a GROUP BY statement, but that doesn't actually group the rows together, because if I run the table through another procedure it performs actions as if each row were not combined. Performing a SELECT * FROM mytable query shows that each row was not actually combined into one.
Is there any way to permanently combine multiple rows into one singular row?
To start, I've got a table like this:
+-------+-----+--------+---------+------+-----+-----------+
| RowID | pID | Name | Date | Code | QTY | Purchased |
+-------+-----+--------+---------+------+-----+-----------+
| 1 | 1 | bob | 9/29/20 | 123 | 1 | |
| 2 | 1 | bob | 8/10/20 | 456 | 1 | |
| 3 | 2 | rob | 9/15/20 | 123 | 1 | |
| 4 | 2 | rob | 9/15/20 | 123 | 1 | |
| 5 | 2 | rob | 9/15/20 | 123 | 1 | |
| 6 | 2 | rob | 9/15/20 | 123 | 1 | |
| 7 | 2 | rob | 9/15/20 | 123 | 1 | |
| 8 | 3 | john | 7/12/20 | 987 | 1 | |
| 9 | 3 | john | 7/12/20 | 987 | 1 | |
| 10 | 4 | george | 9/12/20 | 684 | 1 | |
| 11 | 5 | paul | 2/2/20 | 454 | 1 | |
| 12 | 6 | amy | 1/12/20 | 252 | 1 | |
| 13 | 7 | susan | 5/30/20 | 131 | 1 | |
| 14 | 7 | susan | 6/6/20 | 252 | 1 | |
| 15 | 7 | susan | 5/30/20 | 131 | 1 | |
+-------+-----+--------+---------+------+-----+-----------+
By the end, i'd like to have a table like this:
+-------+-----+--------+---------+------+-----+-----------+
| RowID | pID | Name | Date | Code | QTY | Purchased |
+-------+-----+--------+---------+------+-----+-----------+
| 1 | 1 | bob | 9/29/20 | 123 | 1 | |
| 2 | 1 | bob | 8/10/20 | 456 | 1 | |
| 3 | 2 | rob | 9/15/20 | 123 | 5 | |
| 4 | 3 | john | 7/12/20 | 987 | 2 | |
| 5 | 4 | george | 9/12/20 | 684 | 1 | |
| 6 | 5 | paul | 2/2/20 | 454 | 1 | |
| 7 | 6 | amy | 1/12/20 | 252 | 1 | |
| 8 | 7 | susan | 5/30/20 | 131 | 2 | |
| 9 | 7 | susan | 6/6/20 | 252 | 1 | |
+-------+-----+--------+---------+------+-----+-----------+
Where exactly identical rows are combined into one row, and the QTY field is summed, that I can then add purchases to, or make deductions from the quantity as a total. Using GROUP BY statements can achieve this, but when I go to alter the quantity or add purchases to each person, it treats it like the first table, as if nothing was actually grouped.
So you have this table:
| RowID | pID | Name | Date | Code | QTY | Purchased |
+-------+-----+--------+---------+------+-----+-----------+
| 1 | 1 | bob | 9/29/20 | 123 | 1 | |
| 2 | 1 | bob | 8/10/20 | 456 | 1 | |
| 3 | 2 | rob | 9/15/20 | 123 | 1 | |
| 4 | 2 | rob | 9/15/20 | 123 | 1 | |
| 5 | 2 | rob | 9/15/20 | 123 | 1 | |
| 6 | 2 | rob | 9/15/20 | 123 | 1 | |
| 7 | 2 | rob | 9/15/20 | 123 | 1 | |
| 8 | 3 | john | 7/12/20 | 987 | 1 | |
| 9 | 3 | john | 7/12/20 | 987 | 1 | |
| 10 | 4 | george | 9/12/20 | 684 | 1 | |
| 11 | 5 | paul | 2/2/20 | 454 | 1 | |
| 12 | 6 | amy | 1/12/20 | 252 | 1 | |
| 13 | 7 | susan | 5/30/20 | 131 | 1 | |
| 14 | 7 | susan | 6/6/20 | 252 | 1 | |
| 15 | 7 | susan | 5/30/20 | 131 | 1 | |
The best way, as has been suggested, is to create a new table with the content of your query, then to rename the old table, and the new table to the original table's name, to check if everything is all right, and to drop the original table if yes.
CREATE TABLE indata_new AS
WITH grp AS (
SELECT
MIN(rowid) AS orowid
, pid
, name
, MAX(date) AS date
, code
, SUM(qty) AS qty
FROM indata
GROUP BY
pid
, name
, code
)
SELECT
ROW_NUMBER() OVER(ORDER BY orowid ASC) AS rowid
, *
FROM grp;
ALTER TABLE indata RENAME TO indata_old;
ALTER TABLE indata_new RENAME TO indata;
-- if "indata" now contains the data you want ...
SELECT * FROM indata;
-- out rowid | orowid | pid | name | date | code | qty
-- out -------+--------+-----+--------+------------+------+-----
-- out 1 | 1 | 1 | bob | 2020-09-29 | 123 | 1
-- out 2 | 2 | 1 | bob | 2020-08-10 | 456 | 1
-- out 3 | 3 | 2 | rob | 2020-09-15 | 123 | 5
-- out 4 | 8 | 3 | john | 2020-07-12 | 987 | 2
-- out 5 | 10 | 4 | george | 2020-09-12 | 684 | 1
-- out 6 | 11 | 5 | paul | 2020-02-02 | 454 | 1
-- out 7 | 12 | 6 | amy | 2020-01-12 | 252 | 1
-- out 8 | 13 | 7 | susan | 2020-05-30 | 131 | 2
-- out 9 | 14 | 7 | susan | 2020-06-06 | 252 | 1
-- you can ...
DROP TABLE indata_old;

SQL Query to join 3 tables, resulting a ordered and sorted list

How can I write a Query to join 3 tables, resulting a ordered and sorted list?

I have 3 tables with the following structure
:
Table Users:
|---------------------------|
| Users |
|---------------------------|
| ID | Name |
|-------------|-------------|
| 1 | John |
|-------------|-------------|
| 2 | David |
|-------------|-------------|
| 3 | James |
|-------------|-------------|
| 4 | Jack |
|-------------|-------------|
Table Questions:
|-------------------------------------------------------|
| Questions |
|-------------------------------------------------------|
| ID | Question |
|-------|-----------------------------------------------|
| 1 | How old are you working in this company? |
|-------|-----------------------------------------------|
| 2 | How many customers do you notice? |
|-------|-----------------------------------------------|
| 3 | What is your salary? |
|-------|-----------------------------------------------|
| 4 | Do you speak another language? |
|-------|-----------------------------------------------|
Table Replies
|----------------------------------------|
| Replies |
|----------------------------------------|
| ID | USER ID | QUESTION ID | Reply |
|-----|---------|-------------|----------|
| 1 | 1 | 1 | 10 |
|-----|---------|-------------|----------|
| 2 | 1 | 2 | 30 |
|-----|---------|-------------|----------|
| 3 | 1 | 3 | 3000 |
|-----|---------|-------------|----------|
| 4 | 1 | 4 | yes |
|-----|---------|-------------|----------|
| 5 | 2 | 1 | 7 |
|-----|---------|-------------|----------|
| 6 | 2 | 2 | 25 |
|-----|---------|-------------|----------|
| 7 | 2 | 3 | 1500 |
|-----|---------|-------------|----------|
| 8 | 2 | 4 | no |
|-----|---------|-------------|----------|
| 9 | 3 | 1 | 5 |
|-----|---------|-------------|----------|
| 10 | 3 | 2 | 50 |
|-----|---------|-------------|----------|
| 11 | 3 | 3 | 2000 |
|-----|---------|-------------|----------|
| 12 | 3 | 4 | yes |
|-----|---------|-------------|----------|
| 13 | 4 | 1 | 7 |
|-----|---------|-------------|----------|
| 14 | 4 | 2 | 40 |
|-----|---------|-------------|----------|
| 15 | 4 | 3 | 2000 |
|-----|---------|-------------|----------|
| 16 | 4 | 4 | yes |
|-----|---------|-------------|----------|
I need to write a SQL Query to filter and sort these results.
Almost like an Excel.


Example:
I need to select who speaks another language, who serves from 5 to 100 clients, ordering for the decreasing salary and years in the descending company.

It should result like this:
|--------------------------------------------------------------------|
| Result |
|--------------------------------------------------------------------|
| ORDER | NAME | QUESTION 1 | QUESTION 2 | QUESTION 3 | QUESTION 4 |
|-------|--------|------------|------------|------------|------------|
| 1 | John | 10 | 30 | 3000 | Yes |
|-------|--------|------------|------------|------------|------------|
| 2 | Jack | 7 | 40 | 2000 | Yes |
|-------|--------|------------|------------|------------|------------|
| 3 | James | 5 | 50 | 2000 | Yes |
|-------|--------|------------|------------|------------|------------|
Any suggestions?
Thanks
Do the JOIN with conditional aggregation :
select u.user_id, u.name,
max(case when r.QUESTIONID = 1 then r.reply) as QUESTION1,
max(case when r.QUESTIONID = 2 then r.reply) as QUESTION2,
max(case when r.QUESTIONID = 3 then r.reply) as QUESTION3,
max(case when r.QUESTIONID = 4 then r.reply) as QUESTION4
from Replies r inner join
Users u
on u.user_id = r.user_id
group by u.user_id, u.name;
EDIT :
select t.*
from ( <query> ) t
where . . .;

Select once for duplicate record

This is my tables which I need get data from these tables. Each user can under many type of groups for example 2, 3, 4
1)groups
|gp_id|gp_name|gp_status|
-------------------------------------
| 1 | Admin | Active |
| 2 | R&D | Active |
| 3 | Sales | Active |
| 4 | IT | Active |
2)modules (FK of table parent_modules)
|mod_id| mod_name| pmod_id |
---------------------------------------------
| 1 | name1 | 1 |
| 2 | name2 | 1 |
| 3 | name3 | 2 |
| 4 | name4 | 3 |
| ... | name... | 3 |
mod_id = 5,6,7.. and so on
3)parent_modules
|pmod_id| mod_name |pmod_status|
-----------------------------------------
| 1 | Contact Us | Active |
| 2 | Account Settings | Active |
| 3 | System Settings | Active |
4)group_details
(FK of table groups) (FK of table modules)
|gpd_id | gp_id | mod_id | base_gpd_rule_view/edit/update/insert |
-----------------------------------------------------------------------
| 1 | 3 | 1 | on |
| 2 | 3 | 2 | on |
| 3 | 3 | 3 | |
| 4 | 3 | 4 |
| 5 | 3 | 5 | on |
| 6 | 3 | 6 |
| 7 | 3 | 7 |
| 8 | 4 | 6 |
| 9 | 4 | 7 | on |
| 10 | 4 | 8 | on |
I have to select once only of duplicate results from group_details and this is the result that I want.
| gp_id | mod_id |
----------------------------------------------
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
| 3 | 4 |
| 3 | 5 |
| 3 | 6 |
| 3 | 7 |
| 4 | 8 |
So far this is what I did in my query but it shows me duplicate record.
//get user groups store in database. example group in database:2, 3, 4
$GroupList = explode(", ", $UserDetail['u_group']);
foreach($GroupList as $a)
{
$getParentModuleSQL = base_executeSQL("SELECT * FROM parent_modules WHERE pmod_status<>'Disable'" );
$count1 = base_num_rows($getParentModuleSQL);
while($ParentModuledata_row = base_fetch_array($getParentModuleSQL))
if ($count1!= 0)
{
base_executeSQL("SELECT gp.gp_id AS ID FROM group_details AS gp, modules AS m WHERE m.pmod_id =". $ParentModuledata_row['pmod_id'] ." AND gp.gp_id=". $a ." AND gp.mod_id = m.mod_id" AND (base_gpd_rule_view='on' OR base_gpd_rule_add='on' OR base_gpd_rule_edit='on' OR base_gpd_rule_delete='on') GROUP BY gp.mod_id);
}
}
i still dont understand your problems. if you want all modules a gp_id has access to - select * from group_details where gp_id = XX. If you just want each module, select * from modules.

Picking out specific values from a group in MySQL

This seems like such a simple problem, but I can't find a good solution. I'm trying to select information from a slightly misformatted table. Basically, wherever sequence=0, the person_id should actually be a company_id. This company_id then applies to all the rows which have the same group_id.
Someone thought it was a good idea to format things this way instead of simply having a company_id column, but it makes trying to select by company very difficult. It would make my programming much easier to simply add this extra column, and fix the formatting.
I want to turn something like this:
+----------+------------+-----------+----------+
| group_id | date | person_id | sequence |
+----------+------------+-----------+----------+
| 1 | 2012-08-31 | 10 | 0 |
| 1 | 2012-08-31 | 11 | 1 |
| 1 | 2012-08-31 | 12 | 2 |
| 2 | 1999-04-16 | 10 | 0 |
| 2 | 1999-04-16 | 21 | 1 |
| 2 | 1999-04-16 | 22 | 2 |
| 2 | 1999-04-16 | 23 | 3 |
| 2 | 1999-04-16 | 24 | 4 |
| 3 | 2001-01-09 | 30 | 0 |
| 3 | 2001-01-09 | 31 | 1 |
| 3 | 2001-01-09 | 11 | 2 |
| 3 | 2001-01-09 | 12 | 3 |
+----------+------------+-----------+----------+
Into this:
+------------+----------+------------+-----------+----------+
| company_id | group_id | date | person_id | sequence |
+------------+----------+------------+-----------+----------+
| 10 | 1 | 2012-08-31 | 11 | 1 |
| 10 | 1 | 2012-08-31 | 12 | 2 |
| 10 | 2 | 1999-04-16 | 21 | 1 |
| 10 | 2 | 1999-04-16 | 22 | 2 |
| 10 | 2 | 1999-04-16 | 23 | 3 |
| 10 | 2 | 1999-04-16 | 24 | 4 |
| 30 | 3 | 2001-01-09 | 31 | 1 |
| 30 | 3 | 2001-01-09 | 11 | 2 |
| 30 | 3 | 2001-01-09 | 12 | 3 |
+------------+----------+------------+-----------+----------+
The only way I can think of how to achieve this is with nested SELECT statements, which are very inefficient considering I have about 100M rows. It's a one time fix though, so I don't mind letting it run overnight.
If you permanently want to change your table to include a company_id column then do this:
First alter the table and add the new column:
alter table your_table add company_id int;
Then update all rows to set the company to the person_id = 0 for the group:
UPDATE your_table a
JOIN your_table b ON a.group_id = b.group_id
SET a.company_id = b.person_id
WHERE b.sequence = 0;
And finally remove the rows with sequence = 0:
DELETE FROM your_table WHERE sequence = 0;
Sample SQL Fiddle
The end result will be:
| group_id | date | person_id | sequence | company_id |
|----------|------------|-----------|----------|------------|
| 1 | 2012-08-31 | 11 | 1 | 10 |
| 1 | 2012-08-31 | 12 | 2 | 10 |
| 2 | 1999-04-16 | 21 | 1 | 10 |
| 2 | 1999-04-16 | 22 | 2 | 10 |
| 2 | 1999-04-16 | 23 | 3 | 10 |
| 2 | 1999-04-16 | 24 | 4 | 10 |
| 3 | 2001-01-09 | 31 | 1 | 30 |
| 3 | 2001-01-09 | 11 | 2 | 30 |
| 3 | 2001-01-09 | 12 | 3 | 30 |

Forum MySQL query

I need help with a sql query
I have this table (post_table):
+----+---------+--------+
| id | topicId | userId |
+----+---------+--------+
| 1 | 1 | kalle |
| 2 | 1 | pelle |
| 3 | 2 | laban |
| 4 | 2 | kalle |
| 5 | 2 | kalle |
| 6 | 2 | kalle |
| 7 | 1 | kalle |
| 8 | 1 | kalle |
| 9 | 2 | laban |
| 10 | 1 | laban |
| 11 | 2 | kalle |
| 12 | 2 | kalle |
| 13 | 3 | laban |
| 14 | 1 | kalle |
| 15 | 2 | kalle |
| 16 | 1 | kalle |
| 17 | 3 | laban |
| 18 | 4 | laban |
| 19 | 5 | laban |
| 20 | 6 | pelle |
+----+---------+--------+
I want to get only the rows where userId "kalle" have not read the post, they are
+----+---------+--------+
| id | topicId | userId |
+----+---------+--------+
| 13 | 3 | laban |
| 17 | 3 | laban |
| 18 | 4 | laban |
| 19 | 5 | laban |
| 20 | 6 | pelle |
+----+---------+--------+
topicId 1 was already read by userId "kalle" in id 1,7,8,14
topicId 2 was already read by userId "kalle" in id 4,5,6,11,12,15
And then I also would like to get the unique values in topicId, either id 13 or 17 can show up but not both.
I hope you understand what I'm after
SELECT * FROM POST_TABLE WHERE ID IN
(
SELECT MAX(ID) FROM POST_TABLE WHERE TOPICID NOT IN
(
SELECT DISTINCT TOPICID FROM POST_TABLE WHERE USERID ='kalle'
)
GROUP BY TOPICID
)