Mysql check empty rows of child elements - mysql

I have a query to select all childs (positions) based ob the parent id (order_id):
SELECT * FROM positions WHERE order_id = X
The result looks like:
ID | order_id | checked_1 | checked_2
1 | 1 | J. Doe | M. Doe
2 | 1 | | Mr. Tester
3 | 1 | J. Joe |
Now i need a query to check if the fields checked_1 & checked_2 are not empty of all related childs. So if all fields of the childs are filled, there shoulbe be appear a success notice in frontend.
What is the best way to "migrate" all childs, so that i can afterwards create the php query?

May try this to count if empty value is there:
SELECT count(*) AS count_empty FROM positions
WHERE order_id = X AND (IFNULL(checked_1, '') = '' OR
IFNULL(checked_2, '') = '')
So now, if count_empty is zero you may show the success message.

Try this:
SELECT 'SUCCESS' as '' FROM postions WHERES orderid = x AND (checked_1 IS NOT NULL OR checked_1 = '') AND (checked_2 IS NOT NULL OR checked_2 = '');

We can aggregate here by order, and then check for the presence of an empty checked_1 or checked_2 column, for a given order_id group. You may try the following query:
SELECT
order_id,
CASE WHEN SUM(CASE WHEN COALESCE(checked_1, '') = '' THEN 1 ELSE 0 END) +
SUM(CASE WHEN COALESCE(checked_2, '') = '' THEN 1 ELSE 0 END) = 0
THEN 'valid' ELSE 'invalid' END AS status
FROM positions
WHERE order_id = 1
GROUP BY order_id;
Note that I don't know if the missing values are NULL or actually just empty string. My query covers for both possibilities, but if these missing values be NULL then you can remove my calls to COALESCE. Also, you may remove GROUP BY if you plan on only running this query for a single order_id. Though should you have the need to run the query for multiple orders at a time, what I wrote above should come in handy.

one way to do this:
select MIN(CASE WHEN checked_1 = '' OR checked_2 = '' THEN 0 ELSE 1 END) from positions group by order_id
You will get a '0' if any field is empty or '1' if all are <> ''.
http://sqlfiddle.com/#!9/f7518d/1

Related

SQL - Only return when no rows hold a value

I am looking for a way to return an ID only if NO rows hold a certain value
For example:
*ID* | *Date*
1 | 01/01/2001
1 | 02/02/2002
1 | 03/03/2003
If I want SQL to return the ID only if no dates are equal to 02/02/2002, how would I script that? I have tried and failed with the below:
select *ID*
from (example)
where date != 02/02/2002
The problem is that this still returns the ID - 1, as the first and last row do not equal 02/02/2002. What I am aiming for is no returned results because at least one row held the matching date.
I would need the script to completely skip the ID when there is a matching date in any row.
For clarity the below should return the ID when using the same 'select' as above because no dates are matching:
*ID* | *Date*
2 | 03/03/2003
2 | 04/04/2004
2 | 05/05/2005
You need Group By and Having clause
select ID
From yourtable
group by ID
Having count (case when date != '02/02/2002' then 1 end) = count(*)
As mentioned by mathguy, this also works
select ID
From yourtable
group by ID
Having count(case when date = '02/02/2002' then 1 end) = 0

SQL queries using self-join

I can make simple select, join, update queries. But this seems a bit hard for me (I'm just learning).
Customer has a table like this (using Mysql) (I don't have control on his DB schema, I can't create a Customers table for him. I just need to create some customer reports).
+-----------+--------------+--------------------------+
|Transaction|Customers name|Customers email |Set |
+-----------+--------------+--------------------------+
| 1 | John | jo#gmail.com | blue |
| 2 | Mary | ma#gmail.com | green |
| 3 | Paul | pa#gmail.com | red |
| 4 | JOHN G. | jo#gmail.com | green |
| 5 | Paul Simon | pa#gmail.com | blue |
+-----------+--------------+--------------------------+
As you can see, each transaction the customer enters freely his name. That could lead to apparently more customers, but email field is unique.
I need to make these reports (all of them are driven by what he bought - the 'Set' field):
1) AND searchs (like 'blue' AND 'green')
Customers that have bought 'this' AND 'that' set.
I need to get a result like this:
|John | jo#gmail.com |
or this (as I said, John could enter his name different ways each transaction. If the email is unique, it is fine):
|JOHN G. | jo#gmail.com |
2) OR searchs (like 'blue' or 'red')
Need to get this:
|John | jo#gmail.com |
|Paul | pa#gmail.com |
or this:
|John | jo#gmail.com |
|Paul Simon| pa#gmail.com |
3) Bought one set, but not the other (like 'green' but not 'blue')
|Mary | ma#gmail.com |
Doe anyone know how to do that? I believe this could be accomplished by some kind of 'self join'. But as I'm just a beginner, I couldn't figure out how to solve this.
Obviously a person can buy this or that set and I imagine it is even possible that one person buys the same set again in a later transaction.
So you want information per person. The easiest way is to aggregate you data hence by grouping by person (GROUP BY). Then you check your aggregates in the HAVING clause: Did the customer by set X and / or y?
Query 1:
select email, name
from transactions
group by email
having max(case when set = 'blue' then 1 else 0 end) = 1
and max(case when set = 'green' then 1 else 0 end) = 1;
Query 2:
select email, name
from transactions
group by email
having max(case when set = 'blue' then 1 else 0 end) = 1
or max(case when set = 'red' then 1 else 0 end) = 1;
Query 3:
select email, name
from transactions
group by email
having max(case when set = 'green' then 1 else 0 end) = 1
and max(case when set = 'blue' then 1 else 0 end) = 0;
The name you get with these queries is just one of the matching names arbitrarily chosen. This is something special in MySQL. In standard SQL this would not be allowed. Anyway, whether MySQL or standard SQL, you could also use MIN(name) or MAX(name) to always get the first or last in alphabetical order.
By the way: The CASE WHEN expressions are standard SQL. MySQL, however, features an additional special boolean handling: a true expression evaluates to 1 and a false expression to 0. So in MySQL you can simply write max(set = 'green') = 1 instead of max(case when set = 'green' then 1 else 0 end) = 1.
For first query - I am using sub query here
select name,email from customer c where Set = 'blue' and c.email = (select email from customer where Set = 'green' and email = c.email);
For second query- simple or condition will be enough
select * from customer where Set = 'green' or Set = 'blue' group by email ;
For Third query- (its a work around, as per your requirement it will work,it is based on approach like there should be only 1 record and that record should have Set value as mentioned int the input)
select * from customer group by email having count(pset)= 1 and pset like 'green';

table or column not found in mysql join with selects and cases

So I have this bit of mysql that I'm trying to work out. My goal is to insert the count of a grouping into the primary records to tell me how many of each status is within the related table for the record, so the result might look like this:
| id | name | count1 | count2 |
------------------------------------
| 1 | primary 1 | 5 | 3 |
| 1 | primary 2 | 2 | 7 |
select * from primaryTable
left join (
select
case
when relationTable.relation_status_id = 1
then count(*)
END as count1,
case
when relationTable.relation_status_id = 2
then count(*)
END as count2
) relationTable
on relationTable.primary_id = primaryTable.id
I tried using a subquery to do it, which worked, but requires a select per count, which I'm trying to avoid.
Adding a group by to the subquery resulted in an error that more than one row was being returned.
In the subquery, rather than aggregate COUNT()s inside CASE, you may more easily use SUM() to add up the result of a boolean comparison (0 or 1) to return a result resembling a count.
SELECT
primaryTable.*,
count1,
count2
FROM
primaryTable
JOIN (
SELECT
primary_id,
-- Sum the results of a boolean comparison
SUM(relation_status_id = 1) AS count1,
SUM(relation_status_id = 2) AS count2
FROM relationTable
-- Group in the subquery
GROUP BY primary_id
-- Join the subquery to the main table by primary_id
) counts ON primaryTable.primary_id = counts.primary_id
Note that because MySQL treats the booleans the same as 0 or 1, the comparison relation_status_id = 1 returns 1 or 0. The syntax above isn't supported in every RDBMS. To be more portable, you would need to use a CASE inside SUM() to explicitly return an integer 1 or 0.
SUM(CASE WHEN relation_status_id = 1 THEN 1 ELSE 0 END) AS count1,
SUM(CASE WHEN relation_status_id = 2 THEN 1 ELSE 0 END) AS count2
Your original attempt has some syntax problems. Chiefly, it has no FROM clause, which is causing MySQL to think it should be treated as a scalar value and then complain that it returns more than one row.

MySQL selecting missing rows

I have a table with user info:
user_id | meta_key | meta_value
-----------+----------+-------------
1 | name | John
1 | surname | Doe
2 | name | Luke
3 | name | Jane
I want to select rows with a certain key, given a list of IDs.
If I do:
SELECT meta_value FROM table WHERE meta_key = 'surname' AND user_id IN(1,2,3)
MySQL will only return the info for user 1, since the others do not have surname.
I would like to build a query to return exactly the number of rows as the IDs passed, with NULL (or an empty string) if that particular user has no surname in the table.
I have tried to use IFNULL(meta_value, "") and also using IF, but it does not seem to help.
Any suggestions? I am sure it's a trivial thing but I can't seem to get around this issue.
Here's a SQLfiddle showing the issue: http://sqlfiddle.com/#!9/86eef2/6
My expected output would be:
Doe
NULL
NULL
Try this query:
SELECT DISTINCT user_id,
(SELECT meta_value FROM mytable B WHERE B.user_id = mytable.user_id AND META_KEY = 'surname') AS 'surname_meta_value'
FROM mytable
WHERE user_id IN(1,2,3)
For study purpose, this could be a faster option, in most cases, based on rlb.usa solution:
SELECT user_id,
GROUP_CONCAT(
(CASE WHEN meta_key = "surname"
THEN meta_value
ELSE ''
END) SEPARATOR '')
AS 'surname_meta_value'
FROM mytable WHERE user_id IN(1,2,3)
GROUP BY user_id

mysql: column values as column name in select result

i have a table which i group by one column and then i output the different values and the count of this values.
activty | sum
-------------
Form | 1
Login | 4
Reg | 3
here is an example-code: http://sqlfiddle.com/#!2/c6faf/2/0
but i want the different values tp the column names with one row of values (the count).
like this:
Form | Login | Reg
------------------
1 | 4 | 3
i tried the PIVOT operation, but i'm not getting it working. what am i doing wrong?
here is my code: http://sqlfiddle.com/#!2/c6faf/35/0
thanks in advance!
br
Here you go
SELECT
MAX(CASE activity WHEN 'Form' THEN mysum ELSE NULL END) AS Form,
MAX(CASE activity WHEN 'Login' THEN mysum ELSE NULL END) AS Login,
MAX(CASE activity WHEN 'Reg' THEN mysum ELSE NULL END) AS Reg
FROM (
SELECT activity, COUNT(activity) AS mysum FROM tbl_tracking
WHERE id = 141 AND variant = 2
GROUP BY activity
)sq
But in my opinion such formatting should be done in application layer, not in database layer.
Try this:
SELECT
SUM(IF(activity = 'Form',1,0)) as 'Form',
SUM(IF(activity = 'Login',1,0)) as 'Login',
SUM(IF(activity = 'Reg',1,0)) as 'Reg'
FROM
tbl_tracking
WHERE
id = 141
AND variant = 2
Sql Fiddle here
For this you need to use pivot table concept. here you go for same.
SELECT
SUM((IF(activity='Form',1,0))) as Form,
SUM((IF(activity='Login',1,0))) as Login,
SUM((IF(activity='Reg',1,0))) as Reg
FROM
tbl_tracking
WHERE
id = 141
AND
variant = 2;
fiddle