Mysql Rewrite Column with JSON Array - mysql

I want to write an MySQL Query where I replace a JSON Array with Data from another Table.
I have got two Tables, "Reserved" and "Seats". Reserved contains one column "Seats", an JSON Array referring to the ID of the Table "Seats". Table Seats also contains a column "Name". I now want to basically replace the IDs in the JSON Data of the Seats column of the Reserved Table, with the name of the corresponding IDs stored in the Seats Table.
Is there a way to do this in an Mysql Query. I do not know how to pack a query result in a JSON Format and return it as a column
I already tried to utilize JSON_EXTRACT somehow : see test below.
SELECT * FROM `seats` WHERE ID = JSON_EXTRACT('["276", "277", "278"]','$.*')
Basically I want a Query like this:
SELECT *,
JSONCreate(SELECT name from `seats` WHERE seats.id IN JSON_EXTRACT(reserved.seats)) as name
FROM `reserved`
WHERE 1

You can use one of the following solutions.
solution using JSON_SEARCH and JSON_ARRAYAGG
SELECT r.seats, JSON_ARRAYAGG(s.name)
FROM reserved r JOIN seats s ON JSON_SEARCH(r.seats, 'one', CONVERT(s.id, CHAR(10))) IS NOT NULL
GROUP BY r.seats
solution using ... MEMBER OF () and JSON_ARRAYAGG
SELECT r.seats, JSON_ARRAYAGG(s.name)
FROM reserved r INNER JOIN seats s ON CONVERT(s.id, CHAR) MEMBER OF(r.seats)
GROUP BY r.seats
solution using JSON_CONTAINS and JSON_ARRAYAGG
SELECT r.seats, JSON_ARRAYAGG(s.name)
FROM reserved r INNER JOIN seats s ON JSON_CONTAINS(r.seats, JSON_QUOTE(CONVERT(s.id, CHAR))) = 1
GROUP BY r.seats
You can also use JSON_TABLE to solve this:
SELECT JSON_ARRAYAGG(IFNULL(s.name, ''))
FROM reserved r, JSON_TABLE(
r.seats,
"$[*]" COLUMNS (
id CHAR(50) PATH "$"
)
) AS rr LEFT JOIN seats s ON rr.id = s.id
GROUP BY r.seats
Note: You can use INNER JOIN to remove the empty values. Instead of GROUP BY r.seats you should use a id column.
demo on dbfiddle.uk

Related

Join tables using a value inside a JSONB column postgresql

I have two tables in postgresql.
the first (product) has sku json row ([149461190])
the second (item) has an ordinary sku column
How can I join them on sku?
I tried this, but it didn't work.
cannot recognize input near 'jsonb_to_recordset' '(' 'ps' in joinSourcePart
select * from product ps
, jsonb_to_recordset(ps.sku -> 'ps_sku') as (sku text)
join item v using sku
I hope this query help you, You can see data structure and sample data in dbfiddle
select
*
from
product p
cross join jsonb_array_elements_text(p.sku -> 'ps_sku') as j(sku)
inner join item i on i.sku = j.sku :: numeric

What is the proper MySQL way to take data from 4 rows, 1 column, and separate into 9 columns?

I've studied and tried days worth of SQL queries to find "something" that will work. I have a table, apj32_facileforms_subrecords, that uses 7 columns. All the data I want to display is in 1 column - "value". The "record" displays the number of the entry. The "title" is what I would like to appear in the header row, but that's not as important as "value" to display in 1 row based upon "record" number.
I've tried a lot of CONCAT and various Pivot queries, but nothing seems to do more than "get close" to what I'd like as the end result.
Here's a screen shot of the table:
The output "should" be linear, so that 1 row contains 9 columns:
Project; Zipcode; First Name; Last Name; Address; City; Phone; E-mail; Trade (in that order). And the values in the 9 columns come from "value" as they relate to the "record" number.
I know there are LOT of examples that are similar, but nothing I've found covers taking all the values from "value" and CONCAT to 1 row.
This works to get all the data I want - SELECT record,value FROM apj32_facileforms_subrecords WHERE (record IN (record,value)) ORDER BY record
But the values are still in multiple rows. I can play with that query to get just the values, but I'm still at a loss to get them into 1 row. I'll keep playing with that query to see if I can figure it out before one of the experts here shows me how simple it is to do that.
Any help would be appreciated.
Using SQL to flatten an EAV model representation into a relational representation can be somewhat convoluted, and not very efficient.
Two commonly used approaches are conditional aggregation and correlated subqueries in the SELECT list. Both approaches call out for careful indexing for suitable performance with large sets.
correlated subqueries example
Here's an example of the correlated subquery approach, to get one value of the "zipcode" attribute for some records
SELECT r.id
, ( SELECT v1.value
FROM `apj32_facileforms_subrecords` v1
WHERE v1.record = r.id
AND v1.name = 'zipcode'
ORDER BY v1.value LIMIT 0,1
) AS `Zipcode`
FROM ( SELECT 1 AS id ) r
Extending that, we repeat the correlated subquery, changing the attribute identifier ('firstname' in place of 'zipcode'. looks like we we could also reference it by element, e.g. v2.element = 2
SELECT r.id
, ( SELECT v1.value
FROM `apj32_facileforms_subrecords` v1
WHERE v1.record = r.id
AND v1.name = 'zipcode'
ORDER BY v1.value LIMIT 0,1
) AS `Zipcode`
, ( SELECT v2.value
FROM `apj32_facileforms_subrecords` v2
WHERE v2.record = r.id
AND v2.name = 'firstname'
ORDER BY v2.value LIMIT 0,1
) AS `First Name`
, ( SELECT v3.value
FROM `apj32_facileforms_subrecords` v3
WHERE v3.record = r.id
AND v3.name = 'lastname'
ORDER BY v3.value LIMIT 0,1
) AS `Last Name`
FROM ( SELECT 1 AS id UNION ALL SELECT 2 ) r
returns something like
id Zipcode First Name Last Name
-- ------- ---------- ---------
1 98228 David Bacon
2 98228 David Bacon
conditional aggregation approach example
We can use GROUP BY to collapse multiple rows into one row per entity, and use conditional tests in expressions to "pick out" attribute values with aggregate functions.
SELECT r.id
, MIN(IF(v.name = 'zipcode' ,v.value,NULL)) AS `Zip Code`
, MIN(IF(v.name = 'firstname' ,v.value,NULL)) AS `First Name`
, MIN(IF(v.name = 'lastname' ,v.value,NULL)) AS `Last Name`
FROM ( SELECT 1 AS id UNION ALL SELECT 2 ) r
LEFT
JOIN `apj32_facileforms_subrecords` v
ON v.record = r.id
GROUP
BY r.id
For more portable syntax, we can replace MySQL IF() function with more ANSI standard CASE expression, e.g.
, MIN(CASE v.name WHEN 'zipcode' THEN v.value END) AS `Zip Code`
Note that MySQL does not support SQL Server PIVOT syntax, or Oracle MODEL syntax, or Postgres CROSSTAB or FILTER syntax.
To extend either of these approaches to be dynamic, to return a resultset with a variable number of columns, and variety of column names ... that is not possible in the context of a single SQL statement. We could separately execute SQL statements to retrieve information, that would allow us to dynamically construct a SQL statement of a form show above, with an explicit set of columns to be returned.
The approaches outline above return a more traditional relational model, (individual columns each with a value).
non-relational munge of attributes and values into a single string
If we have some special delimiters, we could munge together a representation of the data using GROUP_CONCAT function
As a rudimentary example:
SELECT r.id
, GROUP_CONCAT(v.title,'=',v.value ORDER BY v.name) AS vals
FROM ( SELECT 1 AS id ) r
LEFT
JOIN `apj32_facileforms_subrecords` v
ON v.record = r.id
AND v.name in ('zipcode','firstname','lastname')
GROUP
BY r.id
To return two columns, something like
id vals
-- ---------------------------------------------------
1 First Name=David,Last Name=Bacon,Zip Code=98228
We need to be aware that the return from GROUP_CONCAT is limited to group_concat_max_len bytes. And here we have just squeezed the balloon, moving the problem to some later processing, to parse the resulting string. If we have any equal signs or commas that appear in the values, it's going to make a mess of parsing the result string. So we will have to properly escape any delimiters that appear in the data, so that GROUP_CONCAT expression is going to get more involved.

multiple query with sqlite 2nd query gives only first result

SELECT quantity, materialTypeId ,
(SELECT typeName
FROM invTypes
WHERE TypeID IN (SELECT materialTypeId
FROM invTypeMaterials
WHERE typeId= 12743
)
) AS material
FROM invTypeMaterials
WHERE TypeID=12743
so this query gives me nice results except the column material. only shows me the first entry instead of giving the name of each row.
if i run these sql seperate they work and i do see what i want. i just need them combined into 2 columns.
what i want to do is, i query one table for data, one of the column has a value wich i want to convert to a name, and that is in another table and its linked by a unique TypeID
Chilly
May be this will work :
SELECT tm.quantity, tm.materialTypeId , t.typeName
FROM invTypeMaterials tm
INNER JOIN invTypes t ON t.TypeID = tm.materialTypeId
WHERE tm.TypeID=12743
If you want to lookup the materialTypeID's name for the current record, you must not use a separate subquery but use the materialTypeID value from the outer query.
This is called a correlated subquery:
SELECT quantity, materialTypeId,
(SELECT typeName
FROM invTypes
WHERE TypeID = invTypeMaterials.materialTypeId
) AS material
FROM invTypeMaterials
WHERE TypeID=12743

Get a list of ids not present in a table

I have a list of ids, and I want to query a mysql table for ids not present in the table.
e.g.
list_of_ids = [1,2,4]
mysql table
id
1
3
5
6
..
Query should return [2,4] because those are the ids not in the table
since we cant view ur code i can only work on asumption
Try this anyway
SELECT id FROM list_of_ids
WHERE id NOT IN (SELECT id
FROM table)
I hope this helps
There is a horrible text-based hack:
SELECT
substr(result,2,length(result)-2) AS notmatched
FROM (
SELECT
#set:=replace(#set,concat(',',id,','),',') AS result
FROM (
select #set:=concat(',',
'1,2,4' -- your list here
,',')
) AS setinit,
tablename --Your tablename here
) AS innerview
ORDER BY LENGTH(result)
LIMIT 1;
If you represent your ids as a derived table, then you can do this directly in SQL:
select list.val
from (select 1 as val union all
select 2 union all
select 4
) list left outer join
t
on t.id = list.val
where t.id is null;
SQL doesn't really have a "list" type, so your question is ambiguous. If you mean a comma separated string, then a text hack might work. If you mean a table, then something like this might work. If you are constructing the SQL statement, I would advise you to go down this route, because it should be more efficient.

Dynamic query string

I want to add some dynamic content in from clause based on one particular column value.
is it possible?
For Example,
SELECT BILL.BILL_NO AS BILLNO,
IF(BILL.PATIENT_ID IS NULL,"CUS.CUSTOMERNAME AS NAME","PAT.PATIENTNAME AS NAME")
FROM
BILL_PATIENT_BILL AS BILL
LEFT JOIN IF(BILL.PATIENT_ID IS NULL," RT_TICKET_CUSTOMER AS CUS ON BILL.CUSTOMER_ID=CUS.ID"," RT_TICKET_PATIENT AS PAT ON BILL.PATIENT_ID=PAT.ID")
But This query is not working.
Here
BILL_PATIENT_BILL table is a common table.
It can have either PATIENT_ID or CUSTOMER_ID. If a particular record has PATIENT_ID i want PATIENTNAME in RT_TICKET_PATIENT as NAME OtherWise it will hold CUSTOMER_ID. If it is i want CUSTOMERNAME as NAME.
Here I m sure That BILL_PATIENT_BILL must have either PATIENT_ID or CUSTOMER_ID.
Can anyone help me?
You can also use IF() to select the right values instead of constructing your query from strings:
SELECT
BILL.BILL_NO AS BILLNO,
IF( BILL.PATIENT_ID IS NULL, cus.CUSTOMERNAME, pat.PATIENTNAME ) AS NAME
FROM
BILL_PATIENT_BILL AS BILL
LEFT JOIN RT_TICKET_CUSTOMER cus ON BILL.CUSTOMER_ID = cus.ID
LEFT JOIN RT_TICKET_PATIENT pat ON BILL.PATIENT_ID = pat.ID
However, it would also be possible to PREPARE a statement from strings and EXECUTE it but this technique is prone to SQL injections, i can only disadvise to do so:
read here: Is it possible to execute a string in MySQL?