MYSQL : clause where with multiple value including null - mysql

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

Related

How can I accomplish the following in SQL?

I have two tables.
table_a:
id | data_x | data_y
--------------------
1 person joe
2 person bob
3 amount 200
4 addres philville
tableB:
map_id | table_a_id
-------------------
7 1
7 3
7 4
8 4
8 2
The result I want is the map_id if it has an entry in table_a for both data_x = 'person' and data_y = '200'
So with the above table B, the result should be
map_id
------
7
How can I write that query in SQL?
This situation is a perfect fit for an unusual SQL operator: INTERSECT. It is a very declarative, efficient and elegant solution for this problem.
SELECT Map.map_id
FROM Table_B AS Map JOIN Table_A AS Person ON (Person.id = Map.table_a_id) AND (Person.data_x = 'person')
INTERSECT
SELECT Map.map_id
FROM Table_B AS Map JOIN Table_A AS Amount ON (Amount.id = Map.table_a_id) AND (Amount.data_y = '200')
Formally what you are asking for is exactly the intersection of two disjoint sets: the set of map id's that are persons and the set of map id's that have a value of 200.
Please note the INTERSECT operator does not exists in MySQL, but it does in almost all advanced relational DBMS, including PostgreSQL.
This is less elegant than the INTERSECT solution #Malta posted, but it works with the limited capabilities of MySQL as well:
SELECT b1.map_id
FROM table_a a1
JOIN tableb b1 ON a1.id = b1.table_a_id AND a1.data_x = 'person'
JOIN tableb b2 ON b2.map_id = b1.map_id AND b2.table_a_id <> b1.table_a_id
JOIN table_a a2 ON a2.id = b2.table_a_id AND a2.data_y = '200';
SQL Fiddle for MySQL.
SQL Fiddle for Postgres.
Based on your input, the following should get you started using MySQL:
SELECT
map_id
FROM TableB
JOIN Table_A
ON TableB.table_a_id = Table_A.id
AND
((Table_A.data_x = 'person')
OR
(Table_A.data_y = '200')
)
GROUP BY map_id
HAVING COUNT(table_a_id) = 2
;
See it in action: SQL Fiddle.
Update
As Erwin Brandstetter made explicit: If the data can't be trusted to be inherently consistent (along the lines of your inquiry), one option is:
SELECT map_id FROM (
SELECT map_id, 'data_x' t
FROM TableB B JOIN Table_A A ON B.table_a_id = A.id AND A.data_x = 'person'
UNION
SELECT map_id, 'data_y'
FROM TableB B JOIN Table_A A ON B.table_a_id = A.id AND A.data_y = '200'
) T
GROUP BY map_id
HAVING COUNT(DISTINCT t) = 2
;
This should ensure "at least one each". (Alternatives have been suggested by others.) To get "exactly one each", you could try
SELECT map_id FROM (
SELECT map_id, 'data_x' t, data_y
FROM TableB B JOIN Table_A A ON B.table_a_id = A.id AND A.data_x = 'person'
UNION
SELECT map_id, 'data_y', data_y
FROM TableB B JOIN Table_A A ON B.table_a_id = A.id AND A.data_y = '200'
) T
GROUP BY map_id
HAVING COUNT(DISTINCT t) = 2 AND COUNT(DISTINCT data_y) = 2
;
See it in action (with additional test data): SQL Fiddle.
And it works in PostgreSQL as well: SQL Fiddle
Please comment if and as this requires adjustment / further detail.
Join the 2 tables, group by map_id, use conditional counting with either count() or sum(), and filter in having clause (I use mysql syntax below):
select map_id,
sum(
case
when a.data_x='person' or a.data_y='200' then 1
else 0
end
) as matches
from a
inner join b on a.id=b.a_id
group by b.map_id
having matches=2
The above query assumes that you cannot have more than one record for any map_id where data_x is person or data_y is 200. If this assumption is incorrect, then you need to use either exists subqueries or 2 derived tables.
Sounds like you want a standard INNER JOIN.
But I do beg to differ on your result:
map_id if it has an entry in table_a for both data_x = 'person' and data_y = '200'
There is not a record in your data set that has both 'person' and data_y = '200' and therefore no mp_id can be returned
Here is a typical INNER JOIN relating to your narrative.
SELECT DISTINCT
b.map_id
FROM
TableA a
INNER JOIN TableB b
ON a.id = b.table_a_id
WHERE
a.data_x = 'person'
AND a.data_y = '200'
If more than one map_id exists with data_x = 'person' and data_y = '200' then you will get multiple results but only 1 row per map_id
If you want the map_id(s) for records with data_x = 'person' or data_y = '200' then switch the and in the where statement to or and you will receive map_id 7 & 8.
SELECT DISTINCT
b.map_id
FROM
TableA a
INNER JOIN TableB b
ON a.id = b.table_a_id
WHERE
a.data_x = 'person'
OR a.data_y = '200'
Note this encompasses (7,1)(8,2) because 1 & 2 both have data_x = 'person' and then (7,3) because 3 has data_y = '200' therefore it would return map_id 7 & 8.
select map_id from
table_b b
left outer join table_a a1 on (b.table_a_id = a1.id and a1.data_x = 'person')
left outer join table_a a2 on (b.table_a_id = a2.id and a2.data_y = '200')
group by map_id
having count(a1.id) > 0 and count(a2.id) > 0
Lets do it simple:
SELECT * FROM
(
SELECT map_id
FROM table_a a1
inner join TableB b1 ON a1.id = b1.table_a_id
where a1.data_x = 'person'
) as p
inner join
(
SELECT map_id
FROM table_a a1
inner join TableB b1 ON a1.id = b1.table_a_id
where a1.data_y = '200'
) as q
on p.map_id = q.map_id
You may replace SELECT * FROM with SELECT p.map_id FROM.
You may add more sub-set-joins to have more conditions.
sql-fiddle

mySQL UNION add column into generated query result

so I have this query
SELECT a.*, b.full_name as salesman
from sales a
LEFT JOIN user b ON a.salesman_id = b.id
WHERE a.deleted_at IS NULL AND (a.status = '1' || a.status = '2' )
AND a.balance <= 0
I want to add another column that is not related to any of the column from the first query. I want to add another column (payment_amount) into the generated result
After googled a while, i come into this query
SELECT a.*, b.full_name as salesman from sales a
LEFT JOIN user b ON a.salesman_id = b.id
WHERE a.deleted_at IS NULL AND (a.status = '1' || a.status = '2' )
AND a.balance <= 0
UNION ALL
SELECT '','','','','','','','','','','','','','','','','','','','','','','','','','','',payment_amount from transaction
However, i cant see payment_amount column next to the generated result.
Please be reminded, that I can't edit the database.
the first query returns 28 columns.
What is the problem here? have been dealing with it for hours.
Any help given is really appreciated. Thank you.
union all will just add rows to your results from previous query. What exactly is ur problem?. Also if you are adding extra column in your 2nd query u need to add on dummy column in first.
SELECT a.*, b.full_name as salesman,"" as payment_amount from sales a
LEFT JOIN user b ON a.salesman_id = b.id
WHERE a.deleted_at IS NULL AND (a.status = '1' || a.status = '2' )
AND a.balance <= 0
UNION ALL
SELECT '','','',... till 28 times,payment_amount from transaction
All rows in the result of a SQL query have the same column names. When you use UNION, the column names are taken from the names/aliases from the first subquery in the union. So in your case, the payment_amount will be in the column named salesman, since that's the corresponding column in the first subquery.
If you want it to be in a column of its own, you can add an extra column 0 AS payment_amount to the first subquery, and an extra '' to the second subquery.
SELECT a.*, b.full_name as salesman, 0 AS payment_amount
from sales a
LEFT JOIN user b ON a.salesman_id = b.id
WHERE a.deleted_at IS NULL AND (a.status = '1' || a.status = '2' )
AND a.balance <= 0
UNION ALL
SELECT '', '','','','','','','','','','','','','','','','','','','','','','','','','','','',payment_amount from transaction

Filter main entity with left join condition

I have 3 tables which are Form, Field and Type.
A Form has multiple fields. A field has a Type.
There are 42 types (it's pure coincidence, I swear). A form always have 42 fields.
So, now, I display a biiig table with all the forms and fields associated. On top of every column, you can filter the results.
My problem is that I can't find a "simple" way of filtering results.
The only way I managed to achieve this, is by making subqueries, like this :
(on this example, I'm filtering on Type.id = 169 with value "secret")
SELECT a.id as fId, f.value, f.type_id
FROM `Form` a
LEFT JOIN Field f ON a.id = f.form_id
WHERE (
SELECT `value` FROM Field f2 WHERE f2.form_id = a.id AND f2.type_id = 169
) = 'secret'
ORDER BY a.date DESC
It's extreme, because for every filter I add, I have a subquery. So, 10 filters = 10 subqueries.
I think there is a better way of doing this, but I can't figure it out, I hope you can help me understand it !
Thanks !
You can use additional JOINs
SELECT a.id as fId, f.value, f.type_id
FROM `Form` a
LEFT JOIN Field f ON a.id = f.form_id
JOIN Field f2 ON f2.form_id = a.id
WHERE f2.type_id = 169
AND f2.value = 'secret'
ORDER BY a.date DESC
If you have more fields to filter on, add additional JOINs for them, and add those conditions to the WHERE clause:
SELECT a.id as fId, f.value, f.type_id
FROM `Form` a
LEFT JOIN Field f ON a.id = f.form_id
JOIN Field f2 ON f2.form_id = a.id
JOIN Field f3 ON f3.form_id = a.id
WHERE f2.type_id = 169
AND f2.value = 'secret'
AND f3.type_id = 55
AND f3.value = 'blah'
ORDER BY a.date DESC
The script that creates the query can add each of these in a loop based on the search criteria.

Find Record if value does not exist?

I am having problem with the SQL Query.
I want to find StatusID = 1 in the records table IF StatusID = 2 does not exist.
I have tried the query:
SELECT * FROM records AS A
LEFT JOIN records AS B on B.StoreID = A.StoreID
WHERE A.StatusID = 1 AND B.StatusID != 2
It is still showing the result even if StatusID = 2 is exist.
Note: StoreID are the ref id in the records table.
You want to use NOT EXISTS:
SELECT *
FROM records AS A
WHERE A.StatusID = 1
AND NOT EXISTS (select B.StoreID
from records AS B
where B.StoreID = A.StoreIDB
and B.StatusID = 2)
Assumming non-null StatusID field (is always filled in):
SELECT * FROM records AS A
LEFT JOIN records AS B
on B.StoreID = A.StoreID
and B.StatusID = 2
WHERE A.StatusID = 1 AND B.StatusID IS NULL
You're still seeing the result even if StatusID = 2 exists because you're using a LEFT JOIN, which as you'll remember returns the entire left set and all matching entries from the right set or a NULL where none exist.
Seems to me the easiest fix is to just use an INNER JOIN, since for the cases LEFT JOIN will return that INNER JOIN won't, StatusID will equal neither 1 nor 2.
Thus:
select * from records a
inner join records b
on b.storeid = a.storeid
where a.statusid = 1
and b.statusid <> 2

GROUP_CONCAT in IN Subquery

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))