MySQL - Select only those that have not another value - mysql

I'd need a query to SELECT all of these entries that are not repeated with another value. I explain the case in the next lines.
The situation
I've got a table of items and values. Each item can be repeated with different values. Let's say I have the following set of records in Table B:
item_id value type_value
ID Item A 0 0
ID Item B 0 0
ID Item A 1 0
ID Item C 1 1
These items, as probably you have already guessed, are IDs, so the "original" items with their information are in another table. What I'm trying to do is to select from the "original" table those items which are in this second table that I've explained.
What I need
As I introduced before, I need to select from a Table A all those items which IDs are IN Table B, but only those which have the value set to 0 and no other record is set to 1 with the same type of "type_value".
Because of the "original" table, I need to do so in a WHERE clause with an INNER SELECT. The result that would be output would be, in this case:
item_id value type_value
ID Item B 0 0
ID Item C 1 1
If we decided to only SELECT those with a specified type_value, I know how to do that, so do not worry about it.
The problem
I am able to do so, at least almost. My problem comes when I have the same item_id with different value fields, so when I try to say "WHERE value != 1", for example, this still gets selected as there is another record with value = 0.
The question
How could I SELECT the rows I'd like in an inner select in a WHERE clause of a main query without having to repeat the whole SELECT with a NOT IN and adding a "WHERE value = 1" to exclude those who have that value?
As it can be a long and complex query (the main one), I'd like to keep it as simple as possible. Of course, as I said before, I can copy the whole query and select those with value set to the one I do not want to, and put a "AND NOT IN" before that SELECT. But that'd repeated code and I think performance could be affected.
Thanks to all of you for your time!
If you need further explanation, please, let me know!
EDIT
Table_A
+----+--------+
| id | name |
+----+--------+
| 1 | Item A |
+----+--------+
| 2 | Item B |
+----+--------+
| 3 | Item C |
+----+--------+
Table_B
+---------+-------+
| item_id | value |
+---------+-------+
| 1 | 0 |
+---------+-------+
| 2 | 0 |
+---------+-------+
| 1 | 1 |
+---------+-------+
| 3 | 1 |
+---------+-------+
Sample query
SELECT name
FROM Table_A
WHERE id IN (SELECT item_id
FROM Table_B
WHERE "item_value is equal to 0 and no other row has this item_id with a item_value different from 0")
Result query
+---------+
| name |
+---------+
| Item B |
+---------+

You could do it by using GROUP BY and HAVING, for example:
SELECT a.*, b.itemid, SUM(b.value) AS vc FROM tableb b
INNER JOIN tablea a ON a.itemid = b.itemid
GROUP BY b.item_id
HAVING vc = 0

I used really simple query structure to make you understand the solution.
There might be better queries to do the job.
Here goes:
First you need to get the rows that you do not want to appear:
This is how it is done :
SELECT item_id,COUNT(item_id) how_many FROM my_table GROUP BY item_id HAVING (how_many>1)
Now you need to select from the table the rows that item_id does not appear in the above query, it is done this way:
SELECT T1.item_id,T1.value,T1.type_value
FROM my_table T1
WHERE (T1.item_id not in (SELECT T.item_id from (SELECT item_id,COUNT(item_id) how_many FROM my_table GROUP BY item_id HAVING (how_many>1))T ))
You can see that I used the "not in" operator and that I named the previous result table - T
Make sure you understand the above
Ask if you need more info

Related

SQL query for filtering all rows with a particular value in at least one row

I have a table T with data like this:
ID | Status
-----------------
1 | X
2 | Y
1 | Z
3 | P
4 | Q
3 | Z
I need to retrieve all the rows where the ID does not have status Z in that or any other rows.
So, for the above example table, I need my query to to return the following table:
ID | Status
-----------------
2 | Y
4 | Q
Rows with ID 1 and 3 were discarded because they had at least one row with the value Z as Status.
What's a good and efficient query for this?
Thanks in advance!
You can exclude the ids with the Z status using the subquery .
select * from datas
where id not in ( select id from datas
where status ='Z' );
Demo: https://www.db-fiddle.com/f/7yUJcuMJPncBBnrExKbzYz/2
As #IVO GELOV suggested you can use EXISTS;
select d.id,d.status
from datas d
where not exists ( select id from datas e
where status ='Z' and d.id=e.id);
Here the inner query will return 1,3 as the id and while checking the returned data the outer query will return 2,4 as the returned id with status
NOT In Query
select id,status from T
where id not in
(select id from T where status='Z');
**OR**
NOT Exists Query
select ext_alias.id,ext_alias.status from T ext_alias
where not exists
(select id from T inn_alias where inn_alias.status='Z' and inn_alias.Id=ext_alias.Id);
You can use any of the above queries but NOT Exists query is faster than Not In query.

How do I select objects when the ID is a small part of another column?

I have two tables, where the IDs found in one table (Table A) can be found in a column of the other table (Table B). The column in Table B that contains the IDs from Table A also contains other information - more IDs from Table A as well as some unrelated data. I cannot devise a query that will select the IDs from Table A that appear in Table B. Example:
Table A
| ID | Name |
| mk5 | Peter|
| j9B | Paul |
| hop | Mary |
Table B
| Type | Settings |
| 1 | x=lmn,y=12a,z=ijg, one_thing=another |
| 2 | x=qza,y=j9B,z=hop, randomtext=and_more |
| 3 | x=hop,y=toe,z=thu, somethingelse=somethingelse |
I thought a query like:
SELECT * FROM TableA WHERE ID IN (SELECT Settings FROM TableB);
would identify IDs from Table A that were in the Settings column of TableB, but I get 0 rows returned. Any advice is greatly appreciated.
If the id of TableA appears after = and right before a , (unless it is at the end) in the Settings column then you can join the tables and use LIKE operator in the ON clause:
SELECT a.*, b.*
FROM TableA a INNER JOIN TableB b
ON concat(b.settings, ',') LIKE concat('%=', a.id, ',%')
FWIW - Forpas's answer helped me, but this query ended up solving it for me:
SELECT a.ID FROM a, b WHERE INSTR(b.Settings, CONCAT('=', a.ID)) != 0
AND INSTR(b.Settings, concat(a.ID, ',')) != 0;
SELECT ID FROM a WHERE ID NOT IN (select a.ID from a, b where
INSTR(b.Settings, CONCAT('=', a.ID)) != 0 AND INSTR(b.Settings, concat(a.ID, ',')) != 0);
First query selects IDs in table a that appear in table b, second query selects IDs in table a that do not appear in table b.
The INSTR(x,y) MySQL function checks for String y in String x, returning a character position if found. If not, returns 0.

mysql select rows including related

I have table:
id | parent | regno | person
1 | 0 | 12 | 5
2 | 1 | 12 | 15
3 | 0 | 13 | 5
4 | 0 | 14 | 6
I have MySQL query...
SELECT *
FROM table
WHERE person='5';
...that returns rows 1 and 3.
In this table row 1 and 2 are related (same regno).
How can i build this query to include related rows?
Basically when searching for person 5 i need MySQL query to return following:
id | parent | regno | person
1 | 0 | 12 | 5
2 | 1 | 12 | 15
3 | 0 | 13 | 5
Parent column has id of column it is related to, but it can be positive and negative integer. All related rows always have same regno.
Thank you.
You want all people who have a regno that is the same as the regno of anyone who is person 5:
--this main query finds all people with the regno from the subquery
SELECT *
FROM table
WHERE regno IN
( --this subquery finds the list of regno
SELECT regno
FROM table
WHERE person = '5'
)
There are other ways to write this; i'm not a fan of IN, and personally would write it like this:
SELECT t.*
FROM table t
INNER JOIN
(
SELECT DISTINCT regno
FROM table
WHERE person = '5'
) u
WHERE t.regno = u.regno
But it's harder to understand, and it's quite likely that these queries would end up being executed identically internally anyway. In this form the DISTINCT is required to make the regno from the subquery unique. If it were not, joined rows would end up duplicated. Why do I prefer it over IN? In some database systems IN's implementation can be very naive and low performing. "Never use IN to create a list longer then you would write by hand" is an old mantra I tend to stick to. This join pattern is also more flexible, can work with multiple values. Not every database supports Oracle-esque where x,y in ((1,3),(3,4)) value multiples
As an aside (and partly in response to the first comment on this answer) it would be more typical and more useful/usual to have the database prepare a set of rows that had parent and child data on the same line
It would look more like this:
SELECT *
FROM
table c
LEFT OUTER JOIN
table p
ON c.regno = p.regno AND p.parent = 1
WHERE c.person = '5' AND c.parent=0
This is assuming your "parent" column is 0 1 indicating true false.. you seem to have made a comment that parent is the id of the relative (not sure if it's parent-of or parent-is)
For a table where there is an id, and parentid column, and the parentid is set to a value when the row is a child of that other id;
id, parentid, name
1, null, Daddy
2, 1, Little Jonny
3, 1, Little Sarah
That looks like:
SELECT *
FROM
table c
INNER JOIN
table p
ON c.parentid = p.id
WHERE p.parentid ID NULL
Rows can have only one parent. A NULL in the parent id defines the row as being a parent, otherwise it's a child. You could turn this logic on its head if you wanted, call the column isparentof and have all child rows with null in the isparentof, and anyone who is a parent of a child, out the child id in isparentof. This then limits you to one child per multiple parents (single child families).. the query to pull them out is broadly the same
You can get all the id values for the person = '5' in a Derived Table.
Now, join back to the main table, matching either the absolute of parent (to get the child row(s)) or the id (to get the parent id row itself).
Based on discussion in comments, Try:
SELECT t.*
FROM your_table AS t
JOIN
(
SELECT id AS parent_id
FROM your_table
WHERE person = '5'
) AS dt
ON dt.parent_id = ABS(t.parent) OR
dt.parent_id = t.id
It is hard to comprehend though, why would you put negative values in parent!

ORDER BY does not work if COUNT is used

I have a table with following content
loan_application
+----+---------+
| id | user_id |
+----+---------+
| 1 | 10 |
| 2 | 10 |
| 3 | 10 |
+----+---------+
I want to fetch 3rd record only if there are 3 records available, in this case i want id 3 and total count must be 3, here is what i expect
+--------------+----+
| COUNT(la.id) | id |
+--------------+----+
| 3 | 3 |
+--------------+----+
Here is the query i tried.
SELECT COUNT(la.id), la.id FROM loan_application la HAVING COUNT(la.id) = 3 ORDER BY la.id DESC;
However this gives me following result
+--------------+----+
| COUNT(la.id) | id |
+--------------+----+
| 3 | 1 |
+--------------+----+
The problem is that it returns id 1 even if i use order by id descending, whereas i am expecting the id to have value of 3, where am i going wrong ?
Thanks.
In your case u can use this query:
SELECT COUNT(la.id), max(la.id) FROM loan_application la
GROUP BY user_id
I try your table in my db MySQL
When you have a group by function (in this instance count()) in the select list without a group by clause, then mysql will return a single record only with the function applied to the whole table.
Mysql under certain configuration settings allow you to include fields in the select loist which are not in the group by clause, nor are aggregated. Mysql pretty much picks up the 1st value it encounters while scanning the data as a value for such fields, in your case the value 1 for id.
If you want to fetch the record where id=count of records within the table, then I would use the following query:
select *
from loan_application
join (select count(*) as numrows from loan_application) t
where id=t.numrows and t.numrows=3
However, this implies that the values within the id field are continuous and there are no gaps.
You are selecting la.id along with an aggregated function (COUNT). So after iterating the first record the la.id is selected but the count goes on. So in this case you will get the first la.id not the last. In order to get the last la.id you need to use the max function on that field.
Here's the updated query:
SELECT
COUNT(la.id),
MAX(la.id)
FROM
loan_application la
GROUP BY user_id
HAVING
COUNT(la.id) = 3
N:B: You are using COUNT without a GROUP BY Function. So this particular aggregated function is applied to the whole table.

getting count of each category filtered by another table's field

I have tables with the following structure.
AD_TABLE -
ID|NAME|CAT_ID|TYPE
1| car | C0101|Sale
2|bike | C0201|Want
CAT_TABLE -
ID |NAME |PARENT|LEVEL
C0100|Vehicle |C0100 | 0
C0101|Car |C0100 | 1
C0200|Bike/Scooters |C0100 | 1
C0201|Bike |C0200 | 2
C0202|Scooter |C0200 | 2
I need to get the count of ADs from each category, I have written the following query.
SELECT `CAT_TABLE`.`ID`,`CAT_TABLE`.`NAME`,`CAT_TABLE`.`LEVEL`,`CAT_TABLE`.`PARENT`, COUNT(`AD_TABLE`.`ID`)
FROM `CAT_TABLE`
LEFT JOIN `AD_TABLE` ON `AD_TABLE`.`CAT_ID`=`CAT_TABLE`.`ID`
WHERE (`CAT_TABLE`.`ID`='C0100' OR `CAT_TABLE`.`PARENT`='C0100') AND `AD_TABLE`.`TYPE`='0'
GROUP BY `CAT_TABLE`.`ID`
I got the count of each categories properly but after including the AD_TABLE.TYPE`='0' in the where clause categories which do not have ADs were ignored. I need to get all the categories even if the count is 0.
try this
SELECT `CAT_TABLE`.`ID`,`CAT_TABLE`.`NAME`,`CAT_TABLE`.`LEVEL`,`CAT_TABLE`.`PARENT`, COUNT(`AD_TABLE`.`ID`)
FROM `CAT_TABLE`
LEFT JOIN `AD_TABLE`
ON `AD_TABLE`.`CAT_ID`=`CAT_TABLE`.`ID`
AND `AD_TABLE`.`TYPE`='0' -- Write and here..<br/>
WHERE (`CAT_TABLE`.`ID`='C0100' OR `CAT_TABLE`.`PARENT`='C0100')
GROUP BY `CAT_TABLE`.`ID`