MySQL LEFT JOIN json field with another id from table - mysql

I have two tables:
Bouquets
+----+------------+
| id | bouquet |
+----+------------+
| 1 | Package #1 |
| 2 | Package #2 |
| 3 | Package #3 |
| 4 | Package #4 |
| 5 | Package #5 |
+----+------------+
And
Prices
+----+----------+-------------------------------------------------------------------+
| id | reseller | price
+----+----------+-------------------------------------------------------------------+
| 1 | 1 | {"1": "1.11", "2": "0.00", "3": "0.00", "4": "4.44", "5": "5.55"} |
+----+----------+-------------------------------------------------------------------+
I need to get bouquet names that price value is not "0.00"...so i try LEFT JOIN to join bouquets.id ON prices.price but i can't get how?
I need to get this:
+----+------------+
| id | bouquet |
+----+------------+
| 1 | Package #1 |
| 4 | Package #4 |
| 5 | Package #5 |
+----+------------+
Here is my try but i im getting empty result:
SELECT b.id, b.bouquet FROM bouquets b
LEFT JOIN prices p ON JSON_CONTAINS(p.price, CAST(b.id as JSON), '$') != "0.00"
WHERE p.reseller=1;

This is not easy to do purely in mysql as it seems, the best idea is to use (PHP,ASP,etc) to do the heavy lifting but after a lot of trial and error I found this post:
Convert JSON array in MySQL to rows
From there this query seems to work for me
SELECT
b.id,
b.bouquet
FROM bouquet AS b
JOIN (
SELECT
indx.id,
indx.idx,
JSON_EXTRACT(p.price, idx) AS bouquetprice
FROM prices AS p
JOIN (
SELECT '$."1"' AS idx, 1 AS id UNION
SELECT '$."2"' AS idx, 2 AS id UNION
SELECT '$."3"' AS idx, 3 AS id UNION
SELECT '$."4"' AS idx, 4 AS id UNION
SELECT '$."5"' AS idx, 5 AS id
) AS indx
WHERE JSON_EXTRACT(p.price, idx) IS NOT NULL
AND p.reseller = 1
) AS ind
ON b.id = ind.id
AND ind.bouquetprice != "0.00"
The trick seems to be that the CONCAT in the linked SO post does not work well with numeric key names in your json. So you have to resort to the 2 indexes in the temporary join to search on.
Also the temporary join table is less than ideal in terms of creating a list of ever growing indexes but it's a place to start at least. (sorry about all the bad naming idx, indx, etc.)
Edit: forgot the reseller part

I im programming in node js using mysql wrapper this is the solution that i use and it is working:
/* QUERY - aaBouquets */
connection.query("SELECT id, bouquet FROM bouquets ORDER BY bouquet ASC",function(err, rows, fields){
/* BOUQUETS - number */
var total = rows.length;
/* FOUND - bouquets */
if (rows.length) {
/* GET - prices */
for (var i in rows) {
var s = 1;
connection.query("SELECT '"+rows[i].id+"' AS id, '"+rows[i].bouquet+"' AS bouquet FROM prices p LEFT JOIN bouquets b ON JSON_SEARCH(p.price, 'one', '$.\""+rows[i].id+"\"') WHERE p.reseller=? AND FORMAT(JSON_EXTRACT(price, '$.\""+rows[i].id+"\"'), 2) != \"0.00\"",[qreseller], function(err, rows, results){
/* CHECK - prices */
if (s < total) {
if (rows.length) {
/* GET - prices */
data.push(rows[0]);
};
s++;
} else {
/* CHECK - prices */
if(data.length) {
if (rows.length) {
/* GET - prices */
data.push(rows[0]);
};
/* RETURN - servers data */
res.json(data);
};
}
});
}
}
});
You can see that first query is getting id and bouquet names then in for loop i im using that id to get values for that bouquet id and show only if value not equal "0.00"..using variable s and total is used here because if i call console.log(data) i get undefined variable..because in node js variable is local and need to be called inside for loop if is called outside i get undefined variable error.
This way i im getting only bouquets with defined price...i don't know if it can be done in single query (because you can't use LEFT JOIN ON b.id on p.prices) so need this two query...to me it is getting ok...so if someone can minimize code to get it more speed or improve...it is welcome.

Call me old-fashioned, but I'm really not a fan of storing json data. Any way, a normalized table might look like this...
Prices
+----------+------------+-------+
| reseller | bouquet_id | price |
+----------+------------+-------+
| 1 | 1 | 1.11 |
| 1 | 4 | 4.44 |
| 1 | 5 | 5.55 |
+----------+------------+-------+

Related

Getting Values From Json Data Inside Array in Mysql

We are saving information in a json Column which contain json data in an array.
Data structure:
[
{
"type":"automated_backfill",
"title":"Walgreens Sales Ad",
"keyword":"Walgreens Sales Ad",
"score":4
},
{
"type":"automated_backfill",
"title":"Nicoderm Coupons",
"keyword":"Nicoderm Coupons",
"score":4
},
{
"type":"automated_backfill",
"title":"Iphone Sales",
"keyword":"Iphone Sales",
"score":3
},
{
"type":"automated_backfill",
"title":"Best Top Load Washers",
"keyword":"Best Top Load Washers",
"score":1
},
{
"type":"automated_backfill",
"title":"Top 10 Best Cell Phones",
"keyword":"Top 10 Best Cell Phones",
"score":1
},
{
"type":"automated_backfill",
"title":"Tv Deals",
"keyword":"Tv Deals",
"score":0
}
]
What we are trying:
SELECT id, ad_meta->'$**.type' FROM window_requests
that returns:
We are looking to get each type as row, which i think only possible with stored procedure, return whole column and then run loop on each row and return data...
Or can you think of a better solution?
Either Update Architecture:
or should we change our database and save information in separate table instead to json column ?
And then we can get easily join to get data with adding a foreign key.
Thanks you.
I understand that you are trying to generate a table structure from the content of your JSON array.
You would need to proceed in two steps :
first, turn each element in the array into a record ; for this, you can generate an inline table of of numbers and use JSON_EXTRACT() to pull up the relevant JSON object.
then, extract the values of each attribute from each object, generating new columns ; the -> operator can be used for this.
Query :
SELECT
id,
rec->'$.type' type,
rec->'$.score' score,
rec->'$.title' title,
rec->'$.keyword' keyword
FROM (
SELECT t.id, JSON_EXTRACT(t.val, CONCAT('$[', x.idx, ']')) AS rec
FROM
mytable t
INNER JOIN (
SELECT 0 AS idx UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) AS x ON JSON_EXTRACT(t.val, CONCAT('$[', x.idx, ']')) IS NOT NULL
) z
This will handle up to 10 objects per JSON array (if you expect more than that, you can add expand the UNION ALL part of the query).
In this DB Fiddle with your test data, this yields :
| id | type | score | title | keyword |
| --- | -------------------- | ----- | ------------------------- | ------------------------- |
| 1 | "automated_backfill" | 4 | "Walgreens Sales Ad" | "Walgreens Sales Ad" |
| 1 | "automated_backfill" | 4 | "Nicoderm Coupons" | "Nicoderm Coupons" |
| 1 | "automated_backfill" | 3 | "Iphone Sales" | "Iphone Sales" |
| 1 | "automated_backfill" | 1 | "Best Top Load Washers" | "Best Top Load Washers" |
| 1 | "automated_backfill" | 1 | "Top 10 Best Cell Phones" | "Top 10 Best Cell Phones" |
| 1 | "automated_backfill" | 0 | "Tv Deals" | "Tv Deals" |
NB : the arrow operator is not available in MariaDB. You can use JSON_EXTRACT() instead, like :
SELECT
id,
JSON_EXTRACT(rec, '$.type') type,
JSON_EXTRACT(rec, '$.score') score,
JSON_EXTRACT(rec, '$.title') title,
JSON_EXTRACT(rec, '$.keyword') keyword
FROM
...

In mysql how can I get only rows from one table which do not link to any rows in another table with a specific ID

I have two tables with the following structures (unnecessary columns trimmed out)
----------------- ---------------------
| mod_personnel | | mod_skills |
| | | |
| - prs_id | | - prs_id |
| - name | | - skl_id |
----------------- | |
---------------------
There may be 0 to many rows in the skills table for each prs_id
What I want is all the personnel records which do NOT have an associated skill record with skill_id 1.
In plain English "I want all the people who do not have the skill x".
Currently, I have only been able to do it with the following nested select. But I am hoping to find a faster way.
SELECT * FROM `mod_personnel` WHERE `prs_id` NOT IN (
SELECT `prs_id` FROM `mod_skills` WHERE `skl_id` = 1 )
This may be faster:
SELECT `mod_personnel`.*
FROM `mod_personnel`
left outer join `mod_skills`
on `mod_skills`.`prs_id` = `mod_personnel`.`prs_id`
and `mod_skills`.`skl_id` = 1
WHERE `mod_skills`.`prs_id` is null;
Using a NOT EXISTS might be faster.
SELECT *
FROM `mod_personnel` p
WHERE NOT EXISTS (SELECT *
FROM `mod_skills` s
WHERE s.`prs_id` = p.`prs_id`
AND s.`skl_id` = 1 );

MySQL Inner Join with No Rows

I have a MySQL database that I normalized and the idea is to allow for a business to select zero or more marketing sequences, but the kicker is that a handful of marketing sequences are required (right now I have 4, but the list can grow). So what I've done is structured my tables as such:
sequence
+-------------+------------------+-------+-------------+----------+
| sequence_id | customer_type_id | title | description | required |
| 1 | 1 | ... | ... | true |
| 2 | 1 | ... | ... | true |
| 3 | 1 | ... | ... | false |
| 4 | 2 | ... | ... | true |
| 5 | 3 | ... | ... | true |
| 6 | 4 | ... | ... | false |
+-------------+------------------+-------+-------------+----------+
business_sequence
+----------------------+-------------+-------------+
| business_sequence_id | business_id | sequence_id |
+----------------------+-------------+-------------+
customer_type_id and business_id are foreign key fields that link to tables that describe the type of customer (customer, former customer, etc.) and the business's information (name, address, etc.) respectively.
The reason why I have the required column in my sequence table is so that if a business decides not to allow for any of the non-required sequences, then that business would not need a row. After all, there's no need to have duplicate rows in the business_sequence table if the only piece of data that is different is the business_id field.
Now what I'm trying to do is get all the rows and all the fields from the sequence table where the business_id in the business_sequence table matches a parameterized value (say 1 for the example that I'm going to show in a second). The query that I tried to use is:
SELECT
s.*
FROM
`sequence` AS s
INNER JOIN `business_sequence` AS b ON b.`sequence_id` = s.`sequence_id`
WHERE
b.`business_id` = 1 AND
s.`required` = true;
But this returned no results if the business had no rows in the sequence table. What I expected it to do is return the 0 rows from the b.business_id = 1 but I also expected it to return the 4 "required" rows (ids: 1, 2, 4, and 5) from the s.required = true.
Whenever I took out the INNER JOIN statement and the business_id portion of the WHERE clause, it does in fact return the 4 "required" rows. This leads me to believe that in my original query, because there are no rows for that particular business_id in the sequence table it isn't returning anything.
With all of this being said, how do I accomplish retrieving the zero or more rows when the business_id field matches the parameterized value and retrieve all of the rows when the required field is true?
How about using OR condition in stead of AND ?
SELECT
s.*
FROM
`sequence` AS s
INNER JOIN `business_sequence` AS b ON b.`sequence_id` = s.`sequence_id`
WHERE
b.`business_id` = 1 OR
s.`required` = true;
I was able to resolve my problem by performing a UNION as such:
SELECT * FROM `sequence` WHERE `required` = true
UNION
SELECT
s.*
FROM
`sequence` AS s
INNER JOIN `business_sequence` AS b ON b.`sequence_id` = s.`sequence_id`
WHERE
b.`business_id` = 1

SELECT from Union x 3 using filter of another table

Background
I have a web application which must remove entries from other tables, filtered through a selection of 'tielists' from table 1 -> item_table 1, table 2, table 3.... now basically my result set is going to be filthy big unless I use a filter statement from another table, using a user_id... so can someone please help me structure my statement as needed? TY!
Tables
cars_belonging_to_user
-----------------------------
ID | user_id | make | model
----------------------------
1 | 1 | Toyota | Camry
2 | 1 |Infinity| Q55
3 | 1 | DMC | DeLorean
4 | 2 | Acura | RSX
Okay, Now the three 'tielists'
name:tielist_one
----------------------------
id | id_of_car | id_x | id_y|
1 | 1 | 12 | 22 |
2 | 2 | 23 | 32 |
-----------------------------
name:tielist_two
-------------------------------
id | id_of_car | id_x | id_z|
1 | 3 | 32 | 22 |
-----------------------------
name: tielist_three
id | id_of_car | id_x | id_a|
1 | 4 | 45 | 2 |
------------------------------
Result Set and Code
echo name_of_tielist_table
// I can structure if statements to echo result sets based upon the name
// Future Methodology: if car_id is in tielist_one, delete id_x from x_table, delete id_y from y_table...
// My output should be a double select base:
--SELECT * tielists from WHERE car_id is 1... output name of tielist... then
--SELECT * from specific_tielist where car_id is 1.....delete x_table, delete y_table...
Considering the list will be massive, and the tielist equally long, I must filter the results where car_id(id) = $variable && user_id = $id....
Side Notes
Only one car id will appear once in any single tielist..
This select statement MUST be filtered with user_id = $variable... (and remember, i'm looking for which car id too)
I MUST HAVE THE NAME of the tielist it comes from able to be echo'd into a variable...
I will only be looking for one single id_of_car at any given time, because this select will be contained in a foreach loop.
I was thinking a union all items would do the trick to select the row, but how can I get the name of the tielist the row is in, and how can the filter be used from the user_id row
If you want performance, I would suggest left outer join instead of union all. This will allow the query to make efficient use of indexes for your purpose.
Based on what you say, a car is in exactly one of the lists. This is important for this method to work. Here is the SQL:
select cu.*,
coalesce(tl1.id_x, tl2.id_x, tl3.id_x) as id_x,
tl1.y, tl2.idz, tl3.id_a,
(case when tl1.id is not null then 'One'
when tl2.id is not null then 'Two'
when tl3.id is not null then 'Three'
end) as TieList
from Cars_Belonging_To_User cu left ouer join
TieList_One tl1
on cu.id_of_car = tl1.id_of_car left outer join
TieList_Two tl2
on cu.id_of_car = tl2.id_of_car left outer join
TieList_Three tl3
on cu.id_of_car = tl3.id_of_car;
You can then add a where clause to filter as you need.
If you have an index on id_of_car for each tielist table, then the performance should be quite good. If the where clause uses an index on the first table, then the joins and where should all be using indexes, and the query will be quite fast.

Hierarchical queries in MySQL

I'm trying to find all the parents, grandparents, etc. of a particular field with any depth. For example, given the below structure, if I provide 5, the values returned should be 1, 2, 3 and 4.
| a | b |
-----------
| 1 | 2 |
| 2 | 3 |
| 3 | 4 |
| 4 | 5 |
| 3 | 6 |
| 4 | 7 |
How would I do this?
SELECT #id :=
(
SELECT senderid
FROM mytable
WHERE receiverid = #id
) AS person
FROM (
SELECT #id := 5
) vars
STRAIGHT_JOIN
mytable
WHERE #id IS NOT NULL
The following answer is not MYSQL-only, but uses PHP. This answer can be useful for all those that end up on this page during their search (as I did) but are not limited to using MYSQL only.
If you have a database with a nested structure of unknown depth, you can print out the contents using a recursive loop:
function goDownALevel($parent){
$children = $parent->getChildren(); //underlying SQL function
if($children != null){
foreach($children as $child){
//Print the child content here
goDownALevel($child);
}
}
}
This function can also be rewritten in any other language like Javascript.