Need help joining 2 tables on one [duplicate] - mysql

This question already has answers here:
How to Display row even if a record does not exist in another table?
(2 answers)
Closed 9 months ago.
So I got an admin dashboard which are not linked by ID but by name. So developer.name is the core name, dateName and absentName are meant to join this developer.name value.
What I got so far is this
db.query("SELECT id, name, absentday, date, slack_id, selected
FROM developers, absent, date
WHERE date.dateName=developers.name
AND absent.absentName=developers.name",
I certainly know this is wrong, anyone have suggestions on how I can make 2 tables join 1 table using only one value?
Developers=
id
DevelopersName
selected
Absent=
absent_id
absent_name
absentdays
Date=
date_id
date_name
date
I have an admin page, and on this page people are able to create an absence day for when they're not present. Which is why I have the table Absence. They only have to put in their name, so lets say Tom Hiddle, and the day they are absent. This will be inserted into the database, This is the same story for the Date table.
Now I have a BOT, Basically, the code and query I've written is meant to exclude the people who are absent on certain days, AND those who are on holidays. I am able to make the Absent table work, correctly excluding the absent people from the query. HOWEVER, I now meet the problem where I have to join DateName and AbsentName to DevelopersName value. Why don't I use IDs for this instance? It's simple, when records are deleted the ID selection will mess up, and since I'm creating this system for a company I work for It cannot have room for error, hence I use identical names to link tables between each other.
So In short, I don't use IDs for multiple reasons, Instead I use identical names between tables. DateName and AbsentName are meant to join with DevelopersName, If I join one of them I get results, If I join both I get none.
// I have a CRUD Dashboard where I can insert absentdays for Developers.
// So lets say Developer Tom Riddle is not present on monday, I'll put his absentday on 1
// Monday to Friday (1-5)
absent = [
{ id: 1, absentName: 'Tom Riddle', absentday: 1},
{ id: 2, absentName: 'Hank Some', absentday: 2},
{ id: 3, absentName: 'Family Man', absentday: 3}
]
// Date is not the same as Absent, Date is a long term deposit of holidays,
// lets say Hank Some Is going on vacation for 3 days!
date = [
{ id: 1, dateName: 'Tom Riddle', date: '2022-05-13'},
{ id: 2, dateName: 'Hank Some', date: '2022-07-14'},
{ id: 3, dateName: 'Hank Some', date: '2022-07-15'},
{ id: 4, dateName: 'Hank Some', date: '2022-07-16'},
{ id: 5, dateName: 'Family Man', date: '2022-06-15'}
]
// This is the core information of the developers. These are NOT the only columns, there are many more, but for sample data I only noted down the most important one's.
developers = [
{ id: 51, developersName: 'Tom Riddle'},
{ id: 52, developersName: 'Hank Some'},
{ id: 53, developersName: 'Family Man'}
]
// Say I run this query
SELECT id, developersName, absentName, absentday FROM developers, absent WHERE absent.absentName=developers.developersName;
// The output will be this
developers = [
{ id: 51, developersName: 'Tom Riddle', absentName: 'Tom Riddle', absentdays: 1},
{ id: 52, developersName: 'Hank Some', absentName: 'Hank Some', absentdays: 2},
{ id: 53, developersName: 'Family Man', absentName: 'Family Man', absentdays: 3}
]
// I now have ABSENT joined with DEVELOPERS.
// With MORE code deeper into the file I can exclude absent people from the query
// Let's say It's monday, It would then look like this.
developers = [
{ id: 52, developersName: 'Hank Some', absentdays: 2},
{ id: 53, developersName: 'Family Man', absentdays: 3}
]
// This WORKS, my issue at the moment is combining DATE and ABSENT on Developers
// My WANTED result is this
developers = [
{ id: 51, developersName: 'Tom Riddle', absentdays: 1, date: '2022-05-13'},
{ id: 52, developersName: 'Hank Some', absentdays: 2, date: '2022-07-14'},
{ id: 52, developersName: 'Hank Some', absentdays: 2, date: '2022-07-15'},
{ id: 52, developersName: 'Hank Some', absentdays: 2, date: '2022-07-16'},
{ id: 53, developersName: 'Family Man', absentdays: 3, date: '2022-06-15'}
]
// After researching myself, Using this query will give these results above ^^
SELECT id, developersName, absentName, absentday FROM developers, absent WHERE absent.absentName=developers.developersName AND date.dateName=developers.developersName;
// With my problem of joining tables being fixed, I run into a new issue.
// Whenever I execute the query above, it will only display people with a DATE
// Let's look at these sample data here.
absent = [
{ id: 1, absentName: 'Tom Riddle', absentday: 1},
{ id: 2, absentName: 'Hank Some', absentday: 2},
{ id: 3, absentName: 'Family Man', absentday: 3},
{ id: 4, absentName: 'Buddy Friend', absentday: 4}
]
// Hank Some no longer has records in these table since his holidays have passed
// Buddy Friend has no holidays at all.
date = [
{ id: 1, dateName: 'Tom Riddle', date: '2022-05-13'},
{ id: 2, dateName: 'Family Man', date: '2022-06-15'}
]
// If I execute the query
SELECT id, developersName, absentday FROM developers, absent WHERE absent.absentName=developers.developersName AND date.dateName=developers.developersName;
// MY results are this
developers = [
{ id: 51, developersName: 'Tom Riddle', absentdays: 1, date: '2022-05-13'},
{ id: 53, developersName: 'Family Man', absentdays: 3, date: '2022-06-15'},
]
// It doesn't display the people who have NO holidays.
// Buddy Friend and Hank Some are MISSING!
So to the question: I need help getting Buddy Friend and Hank Some back in my results. And another question, is there a cleaner way of joining these tables together? Instead of using WHERE, maybe inner joins?

You are using a join syntax that was already out-dated when MySQL was developed. It is weird seeing you using it. Maybe you mistakenly took a book from the 1980s to learn SQL? Better quit this. MySQL still supports this old syntax (as it is still allowed), but we are not using it any longer, because it is less readable than explicit joins, much more prone to errors and doesn't support outer joins.
This is what your query looks like in the syntax we have been using in the last three decades:
SELECT developers.id, developers.developersName, absent.absentday, date.date
FROM developers
INNER JOIN absent ON absent.absentName = developers.developersName
INNER JOIN date ON date.dateName = developers.developersName;
This joins the three tables and returns all matches. If you want to show developers even when they don't have an entry in the absent or date table, use outer joins instead of inner joins:
SELECT developers.id, developers.developersName, absent.absentday, date.date
FROM developers
LEFT OUTER JOIN absent ON absent.absentName = developers.developersName
LEFT OUTER JOIN date ON date.dateName = developers.developersName;
Be aware though that with this data model, names must never change. This may be true for login names, but not for natural names, where names change when people getting married for instance. A more typical database design would hence be:
CREATE TABLE developer
(
developer_id int not null auto_increment,
first_name varchar(100) not null,
last_name varchar(100) not null,
primary key (developer_id)
);
CREATE TABLE developer_absent
(
developer_id int not null,
day_num int not null,
primary key (developer_id, day_num),
foreign key (developer_id) referencing developer (developer_id)
);
CREATE TABLE developer_holiday
(
developer_id int not null,
holiday date not null,
primary key (developer_id, holiday),
foreign key (developer_id) referencing developer (developer_id)
);
The query would then become:
SELECT d.developer_id, d.name, da.day_num, dh.date
FROM developers d
LEFT OUTER JOIN developer_absent da ON da.developer_id = d.developer_id
LEFT OUTER JOIN developer_holiday dh ON dh.developer_id = d.developer_id;
But while this is syntactically correct, it doesn't make much sense semantically, because the absent days and the holidays are not closely related. You'd see that Hank is on holiday on 2022-07-14 and absent on Tuesdays and on holiday on 2022-07-15 and absent on Tuesdays and on holiday on 2022-07-16 and absent on Tuesdays.
A more typical query would be for instance:
-- Who is available on Friday, May 20, 2022
select *
from developer
where developer_id not in
(
select developer_id
from developer_absent
where day_num = (dayofweek(date '2022-05-20') + 5) % 7 + 1
)
and developer_id not in
(
select developer_id
from developer_holiday
where holiday = date '2022-05-20'
);
(There is a little math involved to match your MON-SUN = 1-7 with MySQL's SUN-SAT = 1-7.)

Related

MySQL merge json field with new data while removing duplicates, where the json values are simple scalar values

Suppose that I have a MySQL table with a JSON field that contains only numbers, like this (note: using MySQL 8):
CREATE TABLE my_table (
id int,
some_field json
);
Sample data:
id: 1
some_field: [1, 2, 3, 4, 5]
id: 2
some_field: [3, 6, 7]
id: 3
some_field: null
I would like to merge another array of data with the existing values of some_field, while removing duplicates. I was hoping that this might work, but it didn't:
update my_table set some_field = JSON_MERGE([1, 2, 3], some_field)
The result of this would be:
id: 1
some_field: [1, 2, 3, 4, 5]
id: 2
some_field: [1, 2, 3, 6, 7]
id: 3
some_field: [1, 2, 3]
Considering you have 3 records in your table and you want to merge 1 and 2 as mentioned in your example.
I hope JavaScript is suitable to follow through for you.
// Get both the records
const records = db.execute(“SELECT id, some_field FROM my_table WHERE id=1 OR id=2”);
// You get both the rows.
// Merging row1, you can either use the Set data structure if you’re dealing with numbers like your example, or you could loop using a map and use the spread operator if using JSON. Since your object is an array, I’ll just be explaining to merge 2 arrays.
records[0].some_field = Array.from(new Set(records[0].some_field + record[1].some_field))
// Same for second record.
records[1].some_field = Array.from(new Set(records[0].some_field + record[1].some_field))
// Now update both the records in the database one by one.

Get records with nested items node-mysql

Im using node-mysql for database query manpulation in my expressjs app, And I want to get a nested results from a query. But seems I can't find a way how to do it in a simple way.
In their document I found Joins with overlapping column names
which can query a table name and nested with a columns. However this
is not suitable work around for one-to-many or many-to-many structure.
Following are examples of table structure and expected results .
Tables
tbl_order
id customer_name date
1 Perona 6/7/2018
2 Zorro 6/8/2018
tbl_order_items
id order_id item_name
1 2 compass
2 2 sword
3 2 yakuta
4 1 umbrella
5 1 doll
Expected Results
I want to get all orders and items containing the order.
[
{
id: 1,
customer_name: perona,
data: 6/7/2018,
items: [
{ id: 4, item_name: umbrella },
{ id: 5, item_name: doll },
]
},
{
id: 2,
customer_name: zorro,
data: 6/8/2018,
items: [
{ id: 1, item_name: compass },
{ id: 2, item_name: sword },
{ id: 3, item_name: yakuta },
]
}
]
Is there any option how to do the same in node-mysql?
You can use INNER JOIN to get nested data based on your association.
SELECT [order].*,
[items].[id] AS [items.id]
[items].[item_name] AS [items.item_name]
FROM
(
SELECT [id],
[customer_name],
[date]
FROM [tbl_order]
) AS [order]
INNER JOIN
[tbl_order_items] AS [items]
ON
[order].[id] = [items].[order_id];

Select from two tables and create object with data

I'm trying to fetch data from two MySQL tables and push each into array.
I have to tables item and user with the same column - item_id. Now im requesting the data from them with
var query = "SELECT * FROM items, users WHERE items.item_id=?";
query = connection.format(query,req.params.item_id);
After this, im getting a wall of data. Like this:
{
{ user_id: 13213,
user_name: 'John',
item_id: 1337,
item_name: Leet stuff,
item_price: 13,37
},
{ user_id: 12345,
user_name: 'Mike',
item_id: 1337,
item_name: Leet stuff,
item_price: 13,37
}
But what I actualy need is this:
{
users: [
{ user_id: 13213,
user_name: 'John'
},
{ user_id: 12345,
user_name: 'Mike'
}
],
item_id: 1337,
item_name: Leet stuff,
item_price: 13,37
}
You can't do that in a single query. However, you can use two different queries and combine the output, e.g.:
SELECT u.user_id, u.user_name
FROM users u JOIN ITEMS i ON j.user_id = i.user_id
WHERE i.item_id = ?;
This will give you the list of users. You can then use the below query to get the item details:
SELECT item_id, item_name, item_price
FROM items
WHERE item_id = ?
You can then construct the required structure in your application.

Postgres PLpgSQL JSON SUM

I'm trying to calculate the sum of some JSON values in PLpgSQL (Postgres v9.5.5) but am stuck on the logic.
For this data set:
{
clientOrderId: 'OR836374647',
status: 'PENDING',
clientId: '583b52ede4b1a3668ba0dfff',
sharerId: '583b249417329b5b737ad3ee',
buyerId: 'abcd12345678',
buyerEmail: 'test#test.com',
lineItems: [{
name: faker.commerce.productName(),
description: faker.commerce.department(),
category: 'test',
sku: faker.random.alphaNumeric(),
quantity: 3
price: 40
status: 'PENDING'
}, {
name: faker.commerce.productName(),
description: faker.commerce.department(),
category: 'test',
sku: faker.random.alphaNumeric(),
quantity: 2,
price: 30,
status: 'PENDING'
}
I am trying to get the subtotal of all the lineItems for each row (i.e. quantity * price for each line item, then the sum of these values for the row). So for the above example, the returned value should be 180.
I got this far, but this is returning the totals for all lineItems in the table, not grouped by row.
WITH line_items AS (SELECT jsonb_array_elements(line_items) as line_items FROM public.order),
line_item_totals AS (SELECT line_items->>'quantity' AS quantity, line_items->>'price' AS price FROM line_items)
SELECT (quantity::int * price::numeric) AS sub_total FROM line_item_totals;
I'm sure the fix is simple but I'm not sure how to do this with JSON fields.
Please always include Postgres version you are using. It also looks like your JSON is incorrect. Below is an example of how you can accomplish this with json type and valid json document.
with t(v) as ( VALUES
('{
"clientOrderId": "OR836374647",
"status": "PENDING",
"clientId": "583b52ede4b1a3668ba0dfff",
"sharerId": "583b249417329b5b737ad3ee",
"buyerId": "abcd12345678",
"buyerEmail": "test#test.com",
"lineItems": [{
"name": "name1",
"description": "desc1",
"category": "test",
"sku": "sku1",
"quantity": 3,
"price": 40,
"status": "PENDING"
},
{
"name": "name2",
"description": "desc2",
"category": "test",
"sku": "sku2",
"quantity": 2,
"price": 30,
"status": "PENDING"
}]
}'::JSON)
)
SELECT
v->>'clientOrderId' cId,
sum((item->>'price')::INTEGER * (item->>'quantity')::INTEGER) subtotal
FROM
t,
json_array_elements(v->'lineItems') item
GROUP BY cId;
Result:
cid | subtotal
-------------+----------
OR836374647 | 180
(1 row)

How to "push ahead" records with a given value?

I am using Ruby on Rails 3.2.9 and MySQL. I have an Article model with a user_id attribute (this attribute represents the foreign key - id - for associated author users) and I would like to retrieve articles ordering those "for" a given author. That is, given I have following records:
<#Article id: 1, title: "Title 1", user_id: 1>
<#Article id: 2, title: "Title 2", user_id: 2>
<#Article id: 3, title: "Title 3", user_id: 1>
<#Article id: 4, title: "Title 4", user_id: 3>
<#Article id: 5, title: "Title 5", user_id: 1>
...
<#Article id: N, title: "Title N", user_id: M>
When I look for articles ordered "for" the author with id 1 (user_id = 1) then the returning articles should be ordered as-like the following:
<#Article id: 1, title: "Title 1", user_id: 1>
<#Article id: 3, title: "Title 3", user_id: 1>
<#Article id: 5, title: "Title 5", user_id: 1>
<#Article id: 2, title: "Title 2", user_id: 2>
<#Article id: 4, title: "Title 4", user_id: 3>
...
<#Article id: N, title: "Title N", user_id: M>
In other words, I am looking to retrieve all articles but making those ordered with this "priority": articles created by the given author returned first and then all other articles (that is, I would like to "push ahead" articles created by a given author).
How can I make that?
Note: I am looking for a Ruby on Rails implementation, maybe through the order method.
Your example is not that clear since you are searching for user_id 1 and a normal ordering by user_id would put those first anyway. I believe you mean to do something like:
SELECT id, title, user_id
FROM myTable
ORDER BY CASE WHEN user_id = #search_id THEN 1 ELSE 2 END, user_id
In your example above, #search_id should be 1.
Try:
Article.where(user_id: 1).order("user_id ASC").order("id ASC")
Or this:
Article.where(user_id: 1).order("user_id ASC, id ASC")
Try this ::
Select
*
from
myTable
order by
user_id, id
For a given user_id:
Select
*
from
myTable
where user_id='?'
order by
user_id, id
The query in Rails could be like this:
Article.order(Article.send(:sanitize_sql, ["IF(user_id = ?, 1, 2)", params[:user_id]]))
IF(user_id = ?, 1, 2) returns 1 or 2 for ordering articles. sanitize_sql, a private method, is used to sanitize parameters.
If params[:user_id] is always an integer, the query could be simplified to this:
Article.order("IF(user_id = #{params[:user_id].to_i}, 1, 2)")