MySQL subquery using ALL - mysql

Relevant schema:
courier(courid:int,courname:str)
city(cid:int,cname:str,zid:int) #zid belonging to some table named zone
courierservice(courid:int,cid:int)
So the obvious relationship is courier 'serving' cities. I've been trying to get all couriers serving 'both' cities with cname as A and B.
i.e. a possible intersection.
I could get a workaround using:
select courname from courier where courid in (select courid from courierservice
where cid=(select cid from city where cname='A')) and
courid in (select courid from courierservice where cid=(select cid from city where cname='B'));
But this looks a little heavy.
It should, according to the documentation, work with the following All subquery:
select * from courier where courid in (select courid from courierservice
where cid = all (select cid from city where cname='A' or cname='B'));
But it is returning an Empty Set.
Is there something missing?

It's meaningless to use = ALL: how can a single courierservice.cid simultaneously be equal to more than one city.cid? ALL is only ever used with a comparison operator that can match more than one value, such as >= or <>: see Subqueries with ALL.
However, you would do well to rewrite your query using JOIN (to be both more concise and more performant):
SELECT courname FROM courier NATURAL JOIN (
SELECT courid
FROM courierservice JOIN city USING (cid)
WHERE cname IN ('A', 'B')
GROUP BY courid
HAVING SUM(cname='A') AND SUM(cname='B')
) t

Try replacing cid = all as cid in and it should work well.

ALL() returns a set. But when you compare using '=' it compares with element. So a element can't be compared with a set. If you want to know if that element is inside the set then you have to use IN clause instead of '='

Related

Mysql Rewrite Column with JSON Array

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

The query gives single row query returns more than one row

I'm trying to show staff_code, staff_name and dept_name for those who have taken one book.
Here's my query:
SELECT SM.STAFF_CODE,SM.STAFF_NAME,DM.DEPT_NAME,BT.BOOK_CODE
FROM STAFF_MASTER SM,DEPARTMENT_MASTER DM,BOOK_TRANSACTIONS BT
WHERE SM.DEPT_CODE =DM.DEPT_CODE
AND SM.STAFF_CODE = (
SELECT STAFF_CODE
FROM BOOK_TRANSACTIONS
HAVING COUNT(*) > 1
GROUP BY STAFF_CODE)
It gives the error:
single-row subquery returns more than one row.
How to solve this?
Change = to IN:
WHERE SM.STAFF_CODE IN (SELECT ...)
Because the select returns multiple values, using equals won't work, but IN returns true if any of the values in a list match. The list can be a hard-coded CSV list, or a select with one column like your query is.
That will fix the error, but you also need to remove BOOK_TRANSACTIONS from the table list and remove BOOK_CODE from the select list.
After making these changes, your query would look like this:
SELECT SM.STAFF_CODE,SM.STAFF_NAME,DM.DEPT_NAME
FROM STAFF_MASTER SM,DEPARTMENT_MASTER DM
WHERE SM.DEPT_CODE =DM.DEPT_CODE
AND SM.STAFF_CODE IN (
SELECT STAFF_CODE
FROM BOOK_TRANSACTIONS
HAVING COUNT(*) > 1
GROUP BY STAFF_CODE)
I recommend learning the modern (now over 25 year old) JOIN syntax.

how to get number only from string field mysql

I have field that has value kind of store878 . I would like to have 878 from select statement. How do I get that numbers from select statement
select
store,
address
from
detail,
store_number
where
store (here i would like to have number) = store_mumber.id
I haven't tested this to make sure is works:
SELECT store, address
FROM detail d
INNER JOIN store_number s
ON CAST(SUBSTRING(d.store, LOCATE('%[0-9]%', d.store)) AS int) = s.id
But you should really consider changing the structure of your database.

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

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?