GROUP_CONCAT in IN Subquery - mysql

SELECT A.id, A.title,
FROM (`table`) as A
WHERE A.active = '1'
AND A.id IN (SELECT GROUP_CONCAT(B.id) from B where user = 3)
If i launch subquery SELECT GROUP_CONCAT(B.id) from B where user = 3 only, i obtain 1,2,3,4. But if i launch entire query i obtain only one row.
But if i try to substitute the subquery with its value (1,2,3,4)
SELECT A.id, A.title,
FROM (`table`) as A
WHERE A.active = '1'
AND A.id IN (1,2,3,4)
i obtain the 4 rows ... as i need.
Where is my error ?

MySQL is seeing the subquery return only a single field/row, and therefore treats it as something like:
... and A.id IN ('1,2,3,4')
which boils down to A.id = '1,2,3,4'.
For an 'in' query, there's no need for the group_concat stuff, simply do:
... and A.id IN (select B.id FROM b where user = 3)

SELECT name
FROM test
WHERE salry
IN (
SELECT GROUP_CONCAT( CONCAT('''', salry,'''' ) )
FROM test group by salry
)
this concat will append the resultset with single quotes still its not working like salry will be in resultset '1000','2000','3000','40000' ...

Use FIND_IN_SET()
SELECT A.id, A.title,
FROM (`table`) as A
WHERE A.active = '1'
AND FIND_IN_SET(A.id,(SELECT GROUP_CONCAT(B.id) from B where user = 3))

Related

MYSQL : clause where with multiple value including null

I want to do a select request in mysql, but I don't know how to filter it.
My filter clause is on the column A.present and contain the value 0,1, null. I want to exclude only the 1 value.
My request looks like
Select *
from A,
left join B on A.b_id = B.id
where A.present <> 1
But this request doesn't return the null value. How can I get it?
THank you
In mysq you could try using ifnull in wehere clause
Select *
from A
left join B on A.b_id = B.id
where ifnull(A.present,0) <> 1
or avoiding function you could use
Select *
from A
left join B on A.b_id = B.id
where A.present is not null and A.present <> 1

How can I display the values excluded by the Where clause?

I'm trying to figure out how to to display all those values that are excluded from this WHERE clause instead of those that are chosen by:
$pdo = $service->pdo;
$sql = "SELECT a.id, a.name, a.surname, a.job, b.start_date, b.end_date
FROM table1 a
INNER JOIN table2 b on b.id = a.id
WHERE a.job = '{$job}'
AND
STR_TO_DATE(b.`start_date`, '%d-%m-%Y') = '{$date}'
GROUP BY a.id ";
return $pdo->query($sql)->fetchAll(PDO::FETCH_OBJ);
What this query returns are values that match "work title" first and then values that exist in that date in the table 2. What I'm trying to show are all the other values based on the "work title" excluding those values that exist for that specific date. Basically I need to figure out for example how to know who is free in a specific date based on this query.
Thank you for your help
You can modify your query and set the inverse where clause like this, you get the rows that don't match 'work title' OR (this is important) the rows that don't match the date requeriment in table2:
$pdo = $service->pdo;
$sql = "SELECT a.id, a.name, a.surname, a.job, b.start_date, b.end_date
FROM table1 a
INNER JOIN table2 b on b.id = a.id
WHERE a.job <> '{$job}'
OR
STR_TO_DATE(b.`start_date`, '%d-%m-%Y') <> '{$date}'
GROUP BY a.id ";
return $pdo->query($sql)->fetchAll(PDO::FETCH_OBJ);
EDIT
After reading your comments, i see that you donĀ“t need the roes that your query has dircarded, you need the rows that match the first requeriment and don't match the second one. So you would need it this way:
$pdo = $service->pdo;
$sql = "SELECT a.id, a.name, a.surname, a.job, b.start_date, b.end_date
FROM table1 a
INNER JOIN table2 b on b.id = a.id
WHERE a.job = '{$job}'
AND
STR_TO_DATE(b.`start_date`, '%d-%m-%Y') <> '{$date}'
GROUP BY a.id ";
return $pdo->query($sql)->fetchAll(PDO::FETCH_OBJ);

SQL SELECT to get company status based on working hours

I'm trying to execute a SQL SELECT query to get all the stores within a city and also have a field to inform if the store is within working hours or not.
This is the SQL I have so far:
$day = date('w');
$hour = date('H:i:s');
SELECT a.id, a.rating, a.name, a.image, b.cityName
(
SELECT 1 FROM tbStore a, workingHour b
WHERE a.id = b.id_store
AND b.weekDay = '$day'
AND b.hourOpen <= '$hour'
AND b.hourClose >= '$hour')
) as 'open'
FROM tbStore a, tbCity b
WHERE b.url = '$url'
AND a.id_city = b.id
ORDER BY open
The way I'm checking this value is as a boolean, so the 'open' field needs to be 0 (closed) or 1 (open).
It's kind of working... But the problem is, if just 1 store is within working hours, all of the others will be 'open' as well, instead of just that specific store.
I also saw some sql statements where people use CASE instead of another select, so any other type of code can be used, as long as the final result is correct.
You're redefining the tbStore a and losing the restriction by $url. You probably just need to change:
SELECT 1 FROM tbStore a, workingHour b
To:
SELECT 1 FROM workingHour b
I have just rewrite you query using explict join way
SELECT
a.id
, a.rating
, a.name
, a.image
, b.cityName
, when( c.id_store is not null then 1 else 0 end) as open
FROM tbStore a
inner join tbCity b on a.id_city = b.id
LEFT join workingHour c on a.id = c.id_store
WHERE b.url = '$url'
AND c.weekDay = '$day'
AND c.hourOpen <= '$hour'
AND c.hourClose >= '$hour'
ORDER BY open

ROW Prior to the MAX row, not the MIN

I have the below query to find the row prior to MAX row. i feel like i am missing something, can somebody please help with it. I ammlooking forward to get the b.usercode_1 as row prior to a.usercode_1 not the min or any other random row but the ROW prior to the MAX.
Please suggest.
Select distinct
c.ssn
, c.controlled_group_Status CG_status
, c.last_name || ' , '|| c.first_name FULL_NAME
, a.usercode_1 Current_REG
, a.eff_date effective_since1
, b.usercode_1 PRIOR_REG
, b.eff_date effective_since2
, d.term_eff_date
from employee_eff_date c
, emp_cg_data a
, emp_cg_data b
, emp_ben_elects d
where c.control_id = 'XYZ'
and c.controlled_group_Status <> 'D'
and c.eff_date = (select max( c1.eff_date)
from emp_cg_data c1
where c.control_id = c1.control_id
and c.ssn = c1.ssn)
and a.control_id = c.control_id
and a.ssn = c.ssn
and a.eff_date = (select max(a1.eff_date )
from emp_cg_data a1
where a.control_id = a1.control_id
and a.ssn = a1.ssn)
and a.usercode_1 = 'REG26'
and b.control_id = c.control_id
and b.ssn = c.ssn
and b.eff_date = (select max( b1.eff_date)
from emp_cg_data b1
where b.control_id = b1.control_id
and b.ssn = b1.ssn
and b1.eff_date < a.eff_date)
and b.usercode_1 like 'REG%'
and d.control_id = c.control_id
and d.ssn = c.ssn
and d.life_event_date = (select max( d1.life_event_date)
from emp_ben_elects d1
where d.control_id = d1.control_id
and d.ssn = d1.ssn)
and d.le_seq_no= (select max( d1.le_seq_no)
from emp_ben_elects d1
where d.control_id = d1.control_id
and d.ssn = d1.ssn
and d.life_event_date = d1.life_event_date)
and d.term_eff_date is null
;
NOTE: this is not a complete answer... its a helpful suggestion of what you should start with.
you are doing a Cartesian Product of the four tables, filtered by a WHERE... so something like this
Implicit Join -- generally not a good practice as it can be very difficult to keep the where filters apart from the join conditions.
SELECT *
FROM tableA a, TableB b
WHERE b.id = a.id
another way to write a JOIN (the more generally accepted way)
SELECT *
FROM tableA a
JOIN tableB b ON b.id = a.id
Use the ON clause to join two tables together.
You should change your joins to this format so that others can read your query and understand it better.
suggestion to solve your problem
a fairly simple way to get the second to last row is to use a row counter.
so something like
SELECT *, #row_count := #row_count + 1
FROM tableA a
JOIN tableB b on b.id = a.id AND -- any other conditions for the join.
CROSS JOIN (SELECT #row_count := 0) t
then from here you can get the MAX row, whether thats the ID or something else. and then get the #row_num -1. aka the previous row.

Trying to add one last SUM() column to my query in SQL Server 2008

I have the first query which is producing correct results. What I need is I need to add the sum of values as a last column grouped by surveyid. I can't insert Sum(c.value) into the first query because it is an aggregate function. I have the correct query as my second query below. I know there's pivot functionality but not sure if it can be used here. I do realize that there will be repetition but that's okay.
'first query
SELECT
A.PATIENTID, B.STUDENTNUMBER, c.surveyid,
convert(varchar, A.CreatedDate, 107),
C.QuestionID, C.Value, D.Question
FROM
dbo.Survey A, dbo.Patient B, [dbo].[SurveyQuestionAnswer] C, [dbo].[LookupQuestions] D
WHERE
A.PATIENTID = B.ID
and c.SurveyID = A.ID
and c.QuestionID = d.ID
and c.questionid <> 10
ORDER BY
A.PATIENTID
'second query
select
c.surveyid,SUM(c.value) as scores
from
dbo.SurveyQuestionAnswer c
group by
c.SurveyID
order by
SurveyID '---not important
You can use SUM if you add the OVER clause. In this case:
SELECT
A.PATIENTID, B.STUDENTNUMBER, c.surveyid,
convert(varchar, A.CreatedDate, 107),
C.QuestionID, C.Value, D.Question,
SUM(c.Value) OVER(PARTITION BY c.surveyid) scores
FROM
dbo.Survey A
INNER JOIN dbo.Patient B
ON A.PATIENTID = B.ID
INNER JOIN [dbo].[SurveyQuestionAnswer] C
ON c.SurveyID = A.ID
INNER JOIN [dbo].[LookupQuestions] D
ON c.QuestionID = d.ID
WHERE
c.questionid <> 10
ORDER BY
A.PATIENTID
You could use something like this:
SELECT
s.PATIENTID, p.STUDENTNUMBER, sqa.surveyid,
CONVERT(varchar, s.CreatedDate, 107),
sqa.QuestionID, sqa.Value, lq.Question,
Scores = (SELECT SUM(Value) FROM dbo.SurveyQuestionAnswer s2 WHERE s2.SurveyID = s.ID)
FROM
dbo.Survey s
INNER JOIN
dbo.Patient p ON s.PatientID = p.ID
INNER JOIN
[dbo].[SurveyQuestionAnswer] sqa ON sqa.SurveyID = s.ID
INNER JOIN
[dbo].[LookupQuestions] lq ON sqa.QuestionID = lq.ID
WHERE
sqa.questionid <> 10
ORDER BY
s.PATIENTID
By having a subquery with the SUM(...) you should be able to get that sum as a single value and you don't need to use any grouping function