sql -Extract the second key in a json string - mysql

I have some tables that contains the payment details of students. In one of the tables, a column collects data in json string.
I want to extract the value of amount from the string below. However, you would notice that key 1 has amount and key 2 has amount too.
After extracting amount, I'm obviously getting the values for the two keys, but I want to extract only the amount value in key 2
{"1":{"amount":"500","date":"2023-01-06","amount_discount":"5500","amount_fine":"0","description":"","collected_by":"Super Admin(356)","payment_mode":"Cash","received_by":"1","inv_no":1},
"2":{"amount":"49500","date":"2023-01-22","amount_discount":"0","amount_fine":"0","description":"Being payment for tuition only","collected_by":"Juliet OLAJIDE(S20170181)","payment_mode":"bank_transfer","received_by":"32","inv_no":2}}
Below is my query:
SELECT student_fees_master.*,JSON_EXTRACT(student_fees_deposite.amount_detail, '$.*.amount') AS `deposit`
FROM `student_fees_master`
INNER JOIN fee_session_groups on fee_session_groups.id = student_fees_master.fee_session_group_id
INNER JOIN fee_groups_feetype on fee_groups_feetype.fee_session_group_id = fee_session_groups.id
INNER JOIN fee_groups on fee_groups.id=fee_groups_feetype.fee_groups_id
INNER JOIN feetype on feetype.id=fee_groups_feetype.feetype_id
LEFT JOIN student_fees_deposite on
student_fees_deposite.student_fees_master_id=student_fees_master.id
and student_fees_deposite.fee_groups_feetype_id=fee_groups_feetype.id
WHERE fee_groups_feetype.feetype_id=1
This is what I'm getting:
["500", "49500"]
But I want this:
["49500"]
How do I go about this?

You can do it as follows :
with cte as (
select '{"1":{"amount":"500","date":"2023-01-06","amount_discount":"5500","amount_fine":"0","description":"","collected_by":"Super Admin(356)","payment_mode":"Cash","received_by":"1","inv_no":1},
"2":{"amount":"49500","date":"2023-01-22","amount_discount":"0","amount_fine":"0","description":"Being payment for tuition only","collected_by":"Juliet OLAJIDE(S20170181)","payment_mode":"bank_transfer","received_by":"32","inv_no":2}}' as deposit
)
select JSON_EXTRACT(deposit->"$.*", "$[1].amount") AS `deposit`
from cte;
Demo here

A simple solution might be like this:
ORDER BY deposit DESC LIMIT 1

Related

Access or Mysql - Table rows as columns in query

I have these three tables:
The table "Clienti" contains the customers.
The table "Corsi" contains all the available courses
The table "corsi Fatti" contains all the Courses each client has taken.
what I would need is a query that returns each client, and what courses he attended on what date.
For that I would like to have for example a table returned with these columns:
Clienti.Nome, corsi.row1.corso, corsi.row2.corso, corsi.row3.corso,corsi.rowN.corso.
and the content of the table should be:
clienti.Nome, corsifatti.data of the matching course in the corsi table if present.
so, first column is the client name, and then there is a column for each row of the "corsi" table, and if a client has partecipated on that course then the corsifatti.data should be in that column.
Can something like this be done with a Access or Mysql Query? I have tried with inner joins but the result was not what I need.
select
Clienti.nome, Clienti.Addresse, Clienti.CAP, Clienti.Tel,
Clienti.Ansprechpartner, Clienti.Mail, Clienti.Weiteres,
CorsiFatti.Data, Corsi.Corso, Corsi.Durata
from Clienti
INNER JOIN CorsiFatti on CorsiFatti.[ID Cliente] = Clienti.ID
INNER JOIN Corsi on Corsi.ID = CorsiFatti.[ID Corso]
What you are asking is a simple inner join:
select Clienti.nome, Clienti.Addresse, Clienti.CAP,
Clienti.Tel, Clienti.Ansprechpartner, Clienti.Mail,
Clienti.Weiteres,
CorsiFatti.Data,
Corsi.Corso, Corsi.Durata
from Clienti
INNER JOIN CorsiFatti on CorsiFatti.[ID Cliente] = Clienti.ID
INNER JOIN Corsi on Corsi.ID = CorsiFatti.[ID Corso]
order by Clienti.nome, Corsi.Corso;
I think you meant yours was lacking the order by only.
I wouldn't suggest using access, but if it is access anyway, then you need to have parenthseses around all those joins (peculiar I know, but it is access).
The pivot with variable columns count is complicated. I can advice simplest solution uses JSON aggregation:
SELECT
ClienteId,
Name,
JSON_ARRAYAGG(
JSON_OBJECT("Data", Data, "Corso", Corso)
) Corsi
FROM
CorsiFatti
JOIN Corsi ON Corsi.Id = CorsiFatti.CorsoId
JOIN Clienti ON Clienti.Id = CorsiFatti.clienteId
GROUP BY
ClienteId,
Name
;
Result is row for each client contains client's data at his courses as JSON string:
+===========+=========+========================================================================================+
| ClienteId | Name | Corsi |
+===========+=========+========================================================================================+
| 1 | Mr. Fix | [{"Data": "2021-01-01", "Corso": "Corso1"}, {"Data": "2021-02-01", "Corso": "Corso3"}] |
+-----------+---------+----------------------------------------------------------------------------------------+
Here you can find SQL fiddle

How can I compare the sum of one field to another single field?

My data set is like so:
Total_Order_Table
Order_no (unique)
Shipped_quantity
Order_Detail_Table
Order_number (not unique)
Quantity_per_bundle
I need to take the sum of Quantity_per_bundle for each order_number from Order_Detail_Table and compare it to the Shipped_quantity.
My idea is an outer join so that my data will look like so:
I need to be able to see quantity discrepancies and if the order number exists in both tables.
Thanks in advance!
Normaly with a FULL OUTER JOIN in sql:
SELECT to.Order_no AS Order_no_Total_Order_Table, od.Order_number AS Order_No_Ordr_detail_Table, SUM(od.Order_number) AS sum_Quanitty_Per_Bundle, od.Order_number
FROM Total_Order_Table AS to
FULL OUTER JOIN Order_Detail_Table AS od ON to.Order_no = od.Order_number
GROUP BY to.Order_no
But FULL OUTER JOIN don't exist in mysql. But you can simulate it : http://www.xaprb.com/blog/2006/05/26/how-to-write-full-outer-join-in-mysql/
Unless I'm missing something subtle in the question, isn't this just as simple as a SELECT query with a join between the tables.
Something along the lines of this should achieve the result:
SELECT tot.Order_no,
odt.Order_no,
SUM(odt.Quantity_per_bundle),
tot.Shipped_quantity
FROM Total_Order_Table tot
LEFT JOIN Order_Detail_Table odt ON odt.Order_Number = tot.Order_Number
GROUP BY tot.Order_no, odt.Order_no, tot.shipped_quantity
(Code not tested in MySQL so forgive errors)

mysql regex not returning correct results

I have a table called lis_pendens and a table called docket_entries. Each lis_pendens record has many docket_entries, where the foreign key of docket_entries is case_id. If the description field of docket_entries contains the text 'writ' or 'title' or 'sale' or 'dismissal', then I do not want to return the associated lis_penden record in the result set. Otherwise, if none of the docket_entries belonging to a lis_pendnes record contain these keywords, then I want to return the lis_pendens record in the result set.
Here is the query I have created:
SELECT lis_pendens.id as id
FROM lis_pendens
INNER JOIN docket_entries
ON docket_entries.case_id = lis_pendens.id
WHERE no_foreclosure_sale_created_at IS NULL
AND NOT EXISTS (
SELECT 1 FROM docket_entries AS e
WHERE e.case_id = lis_pendens.id
AND LOWER(e.description) REGEXP 'writ|title|sale|dismissal'
) GROUP BY id
I thought the query was good but it's producing EVERY lis_pendens record in the database, even though some of those lis_pendens records have associating docket_entries whose description field contains this:
Notice of Voluntary Dismissal
Notice of Dismissal & Discharge of Lis Pendens
Certificate of Title
...
Obviously, something is wrong with the query. What am I doing wrong?
You could use MAX() to perform a group-wise NAND operation:
SELECT p.id
FROM lis_pendens p
INNER JOIN docket_entries e ON e.case_id = p.id
GROUP BY p.id
HAVING MAX(LOWER(e.description) REGEXP 'writ|title|sale|dismissal') = 0;

LEFT JOIN query returns multiple results

I have the query below which looks at a table of data in which each line has an ID, date/time and key action (as well as other data). This table cannot be changed and is out of my control.
The query finds an occurrence of the Create action (which is always the first) pulls the ID and Date/Time for Creation and then pulls the Date/Time of the other key actions (Add Info, Book Appt, Accept) in one row of data:
SELECT _create.ID AS ID,
_create.`datetime` AS Create,
_inform.`datetime` AS Add_Info,
_bookap.`datetime` AS Book_Appt,
_accept.`datetime` AS Accept,
FROM table AS _create
LEFT JOIN table AS _inform ON (_create.ID = _inform.ID AND _inform.action = 'Add Info')
LEFT JOIN table AS _bookap ON (_create.ID = _bookap.ID AND _bookap.action = 'Book Appt')
LEFT JOIN table AS _accept ON (_create.ID = _accept.ID AND _accept.action = 'Accept')
WHERE _create.action="Create"
So I get something like:
ID - Create Date - Inform Date - Bookap Date - Accept Date
1234 01/02/2013 02/02/2013 09/02/2013 10/02/2013
This works well.
However if the query finds 2 events of the same type ie 'Book Appt' for the same ID, which can happen sometimes, it pulls two lines of data for that ID. So I get:
ID - Create Date - Inform Date - Bookap Date - Accept Date
1234 01/02/2013 02/02/2013 09/02/2013 10/02/2013
1234 01/02/2013 02/02/2013 15/02/2013 10/02/2013
I need it to ignore the second occurence and only return one line per ID. Or, even better return a line showing Bookap Date1 and Bookap Date2.
Any ideas?
Use GROUP BY to group the records into one row, and min() to select the earliest event date.
SELECT _create.ID AS ID,
min(_create.`datetime`) AS Create,
min(_inform.`datetime`) AS Add_Info,
min(_bookap.`datetime`) AS Book_Appt,
min(_accept.`datetime`) AS Accept,
FROM table AS _create
LEFT JOIN table AS _inform ON (_create.ID = _inform.ID AND _inform.action = 'Add Info')
LEFT JOIN table AS _bookap ON (_create.ID = _bookap.ID AND _bookap.action = 'Book Appt')
LEFT JOIN table AS _accept ON (_create.ID = _accept.ID AND _accept.action = 'Accept')
WHERE _create.action="Create"
GROUP BY _create.ID;
A quick and dirty way to show all event dates but still return one row is to use the group_concat() function instead of min() in the query above. This would put multiple datetimes into single columns which your app layer would then need to parse.
Sure, use GROUP BY _create.ID after your WHERE clause.
If you want to return multiple Book_Appt values in a single row, you can use GROUP_CONCAT like this:
SELECT _create.ID AS ID,
_create.`datetime` AS Create,
_inform.`datetime` AS Add_Info,
GROUP_CONCAT(_bookap.`datetime`) AS Book_Appt,
_accept.`datetime` AS Accept,
FROM table AS _create
LEFT JOIN table AS _inform ON (_create.ID = _inform.ID AND _inform.action = 'Add Info')
LEFT JOIN table AS _bookap ON (_create.ID = _bookap.ID AND _bookap.action = 'Book Appt')
LEFT JOIN table AS _accept ON (_create.ID = _accept.ID AND _accept.action = 'Accept')
WHERE _create.action="Create"
GROUP BY `ID`
This would return a comma-seperated list of datetime values in your case.
So your output might look like
ID - Create Date - Inform Date - Bookap Date - Accept Date
1234 01/02/2013 02/02/2013 09/02/2013,15/02/2013 10/02/2013
For your first question (to return only one row) I would use an ORDER BY clause to sort your result set, followed by a LIMIT 1 expression to only return one row.

sql query returns incorrect result

I have a mysql database that stores quotation documents with some products that are clearly defining the price of each product in them, and a table for contracts storing contract details as well as customer code and quotation code to which it belongs. I have the following query to see how much is the total price of the quotation to write it in the invoice:
select
sum(sqproducts.price * sqproducts.quantity) as 'total-price',
squotations.currency as 'currency'
from
sqproducts,
ccontracts,
squotations
where
sqproducts.contracted=1
AND squotations.code=sqproducts.quotation_code
AND sqproducts.quotation_code=ccontracts.squotation_code
AND sqproducts.quotation_code='QUOT/2012/1'
group by
currency
Its a blind hit , Try this one:
select
sum(sqproducts.price * sqproducts.quantity) as 'total-price',
squotations.currency as 'currency'
from
sqproducts
inner join squotations on (squotations.code=sqproducts.quotation_code)
inner join ccontracts on (sqproducts.quotation_code=ccontracts.squotation_code)
where
sqproducts.contracted=1
AND sqproducts.quotation_code='QUOT/2012/1'
group by
squotations.currency