Mysql interesting query - mysql

I have the next table:
+---------+------------+
| firm_id | service_id |
+---------+------------+
| 6 | 2 |
| 6 | 4 |
| 23 | 7 |
| 23 | 6 |
I want to get ONLY companies who do not have service_id=4 in their service list.
A query of the above table should return only the company with firm_id=23 because firm_id=6 has one record with service_id=4.
I want to make it with one query. Is this possible (without joins)?
Thanks.
P.S. Thanks everyone. User "derobert" suggested very interesting way, what i was looking for.

You can do it several ways. Here is one, with a correlated subquery:
SELECT DISTINCT firm_id FROM table t1
WHERE NOT EXISTS ( SELECT 1 FROM table t2 WHERE t1.firm_id = t2.firm_id AND t2.service_id = 4)
In MySQL-land it is often better to rewrite as a self-join:
SELECT DISTINCT firm_id
FROM table t1 LEFT JOIN table t2 ON (t1.firm_id = t2.firm_id AND t2.service_id = 4)
WHERE t2.firm_id IS NULL
Finally, here is one way to do it that doesn't involve subqueries or joins (but I expect performs worse than either of the above)
SELECT firm_id, CONCAT(',', GROUP_CONCAT(service_id SEPARATOR ','), ',') AS service_ids
FROM table t1
GROUP BY firm_id
HAVING service_ids NOT LIKE '%,4,%'
I confess I haven't actually run these; please forgive typos.

SELECT DISTINCT
firm_id
FROM
TableX AS t
WHERE
NOT EXISTS
( SELECT
*
FROM
TableX AS s
WHERE
s.firm_id = t.firm_id
AND
s.service_id = 4
)
or:
SELECT
firm_id
FROM
TableX
GROUP BY
firm_id
HAVING
COUNT(service_id = 4) = 0

You could try
SELECT * FROM your_table
WHERE firm_id NOT IN
(SELECT DISTINCT firm_id FROM your_table
WHERE service_id = 4) a
As suggested by ypercube (thanks!) you can also try
SELECT DISTINCT firm_id, service_id FROM your_table
WHERE firm_id NOT IN
(SELECT firm_id FROM your_table
WHERE service_id = 4) a

Related

Find Values between two different columns in a table

table one
+----------------------+
|column A | Column B|
| 2 | 4 |
| 3 | 5 |
| 1 | 2 |
| 1 | 2 |
| 8 | 7 |
+----------------------+
Output
+-------+
|1 | 2 |
|1 | 2 |
+-------+
i want to print only the above output without COUNT, and any duplicate record example? please help
how about below where cluase
select * from t where columnA=1 and columnB=2
or
select columnA,columnB from t
group by columnA,columnB
having count(*)>1
or you can use exists
select t1.* from t t1 where exists
(select 1 from t t2 where t2.columnA=t1.columnA
and t2.columnB=t1.columnB group by columnA,columnB
having count(*)>1
)
You possibly want only those rows which are duplicate. If you don't have Window Functions available in your MySQL version, you can do the following:
SELECT
t.*
FROM your_table AS t
JOIN (SELECT columnA, columnB
FROM your_table
GROUP BY columnA, columnB
HAVING COUNT(*) > 1) AS dt
ON dt.columnA = t.columnA AND dt.columnB = t.columnB
Details: In a Derived table, we get all those combination of columnA and columnB which have more than one row(s) (HAVING COUNT(*) > 1).
Now, we simply join this result-set back to the main table, to get those rows only.
Note: This approach would not be needed if you want to fetch only these two columns. A simple Group By with Having would suffice, as suggested in other answer(s). However, if you have more columns in the table, and you will need to fetch all of them, and not just the columns (used to determine duplicates); you will need to use this approach.
You can use in operator with a grouped subquery as :
select *
from tab
where ( columnA, columnB) in
(
select columnA, count(columnA)
from tab
group by columnA
);
or use a self-join as :
select t1.columnA, t1.columnB
from tab t1
join
(
select columnA, count(columnA) as columnB
from tab
group by columnA
) t2
on ( t1.columnA = t2.columnA and t1.columnB = t2.columnB );
Rextester Demo
I would use EXISTS, if the table has primary column :
SELECT t.*
FROM table t
WHERE EXISTS (SELECT 1 FROM table t1 WHERE t1.col1 = t.col1 AND t1.col2 = t.col2 AND t1.pk <> t.pk);

Find the same sets of pairs

I have such scheme in mysql:
TableA (id integer PK, pid integer, mid integer)
Ex. data:
id | pid | mid
1 | 2 | 2
2 | 2 | 4
3 | 3 | 4
4 | 4 | 2
5 | 4 | 4
6 | 3 | 2
7 | 3 | 5
I have pid with some mid's and want to find all pid's with the same set of mid's. In example for pid=2 answer is 2,4
group_concat is not suitable for me
I think it should be simple, but the answer eludes me
UPD:
I have tried group_concat:
SELECT DISTINCT(b.pid) FROM (SELECT pid, group_concat(mid) as concated FROM TableA where pid=100293) as a, (select pid, group_concat(mid) as concated, COUNT(1) as count FROM TableA group by pid) as b where a.concated=b.concated;
Since you are working with integers, instead of group_concat you could generate a bitmask on distinct mid values for each pid and join on that. Then it's just math all the way down:
SELECT DISTINCT pid
FROM (SELECT pid, sum(pow(2,mid)) as midmask FROM (SELECT distinct pid, mid FROM tableA) as t1a GROUP BY pid) as t1
INNER JOIN (SELECT pid, sum(pow(2,mid)) as midmask FROM (SELECT distinct pid, mid FROM tableA) as t2a GROUP BY pid) as t2
ON t1.midmask = t2.midmask
IF mid is already distinct for each pid then you can get rid of the inner-inner subqueries.
Using #GordonLinoff's excellent single-subquery approach where GROUP_CONCAT is only used on the main query (where it won't be so expensive). Instead of the group_concat on the inner query we use the bitmask approach that may be quicker.
SELECT midmask>>1, group_concat(pid)
FROM (SELECT pid, sum(pow(2,mid)) as midmask FROM (SELECT distinct pid, mid FROM tableA) as t1a GROUP BY pid) as t1
GROUP BY midmask;
Results:
+---------+-------------------+
| midmask | group_concat(pid) |
+---------+-------------------+
| 10 | 2,4 |
| 26 | 3 |
+---------+-------------------+
Obviously that midmask in the result set isn't super necessary, but you can pick out the values from the bitmask if you want to see the mid values that contributed to the match if you like.
I'm using the bit right-shift operator to insure that the proper bit is set in the midmask result otherwise you'll be off by one. If you don't care about the output of the midmask, then don't bother with the >>1 portion of the query.
You can use this query. It will give you comma separated pids.
select `mid`, group_concat(`pid`) from `tableA` group by `mid`;
In MySQL, I would approach this using group_concat():
select mids, group_concat(pid)
from (select pid, group_concat(mid order by mid) as mids
from t
group by pid
) t
group by mids;
This solves the general problem, for all pids. Solving for 1 pid is a bit tricky in MySQL (no window functions), but you can try:
select t.pid, t2.pid, count(*)
from t join
t t2
on t.mid = t2.mid and t2.pid = 2
group by t.pid, t2.pid
having count(*) = (select count(*) from t where t.pid = t.pid) and
count(*) = (select count(*) from t where t.pid = t2.pid);
For this, you want indexes on t(mid, pid) and t(pid).

Issue with a SELECT statement within a IN

I'm trying to select a bunch of rows from my first table depending of values from my second table.
To realize that, I came up with the following query :
SELECT *
FROM table2 t2
WHERE t2.id IN(
SELECT subquery.id
FROM
(
SELECT id
FROM table1 t1
WHERE (t1.property=10 AND t1.value=0)
) AS subquery
INNER JOIN
(
SELECT id
FROM table1 t1
WHERE (t1.property=20 AND i.value=1)
) AS subquery2
on subquery.id=subquery2.id
)
Depending of the property and value from table1, a specific list of id will be selected to be used as a condition for the final SELECT with the table2.
The subquery in itself is working, I tested it and I could retrieve the good id.
+------+
| id |
|------|
| 18 |
| 55 |
¦ ¦
Problem is, the IN isn't working, at the end of the final SELECT I retrieved all the rows from table2 instead of the ones respecting the condition of having the same id as the subquery retrieved earlier.
+------+------+---
| id | name |
|------|------|---
| 1 | xx |
| 2 | yy |
¦ ¦ ¦
So my question is :
Why is the subquery ignored in my IN statement and is there a way to bypass that problem ?
EDIT
Here is a SQLFiddle as requested, but strangely, the code I posted is working here but not for my database.
SELECT * FROM table2 t2
INNER JOIN
(SELECT
subquery.id
FROM
(SELECT
id
FROM
table1 t1
WHERE
(t1.property = 10 AND t1.value = 0)) AS subquery
INNER JOIN
(SELECT
id
FROM
table1 t1
WHERE
(t1.property = 20 AND i.value = 1)) AS subquery2 ON subquery.id = subquery2.id)Z ON Z.id=t2.id
You can try above code.
Hope this will help.
try like the following; if you are having any null values in the columnn you won't get the expected result.
common_id not in
(
select common_id from Table1
where common_id is not null
)
SQL "select where not in subquery" returns no results

MySQL find duplicates in multiple columns

I have a table with user IDs split into 2 columns. (To explain this a little more, we capture the IDs of participants by scanning barcodes. Sometimes the barcode scanner function doesn't work for whatever reason, so we also allow manual entry of the ID, IF the barcode scanner doesn't work.) This results in data like the following:
+------+-----------+
| ID | ID_MANUAL |
+------+-----------+
| A | NULL |
| NULL | A |
| B | NULL |
| B | NULL |
| NULL | C |
| C | NULL |
| NULL | D |
| NULL | D |
+------+-----------+
I want to find all of the duplicate IDs, taking both columns into account. It's easy to find the duplicates that are only in 1 column ("B" and "D"). But how do I find the duplicates "A" and "C"? Ideally, the query would find and return ALL duplicates (A,B,C, and D).
Thanks!
Try this:
SELECT DUP.* FROM (SELECT ID FROM yourtable) ORI
LEFT JOIN yourtable DUP ON DUP.ID = ORI.ID_MANUAL WHERE DUP.ID IS NOT NULL
An advice: a field named ID m,ust be unique and not null. But if you have this structure, you can try this:
SELECT id
FROM yourtable t
WHERE id is not null
AND
(SELECT COUNT(*)
FROM yourtable t2
WHERE t2.id = t.id) +
(SELECT COUNT(*)
FROM yourtable t3
WHERE t3.id_manual = t.id) > 1
UNION
SELECT id_manual
FROM yourtable t
WHERE id_manual is not null
AND
(SELECT COUNT(*)
FROM yourtable t2
WHERE t2.id = t.id_manual) +
(SELECT COUNT(*)
FROM yourtable t3
WHERE t3.id_manual = t.id_manual) > 1
You can go on Sql Fiddle
You could try UNION ALL here:
select id,count(*)
from
(
select id
from yourtable
union all
select id_manual as id
from yourtable
) a
group by id
having count(*) >1;
try:
select id, count(*)
from
(
select id
from data
where id_manual is null
union all
select id_manual as id
from data
where id is null
) a
group by id
having count(*) > 1;
and
select id, id_manual
from data
group by id, id_manual
having count(*) > 1;
You can do this with a simple JOIN, using COALESCE and DISTINCT if you have a surrogate auto-increment primary key:
SELECT DISTINCT s2.pk, s2.ID, s2.ID_MANUAL
FROM scans s1
JOIN scans s2
ON COALESCE(s2.ID, s2.ID_MANUAL) = COALESCE(s1.ID, s1.ID_MANUAL)
AND s2.pk > s1.pk
This will exclude the original record, so you could delete the records returned in this result set.
Here's the SQL Fiddle.

MySql select next lower number without using limit

Is it possible to select the next lower number from a table without using limit.
Eg: If my table had 10, 3, 2 , 1 I'm trying to select * from table where col > 10.
The result I'm expecting is 3. I know I can use limit 1, but can it be done without that?
Try
SELECT MAX(no) no
FROM table1
WHERE no < 10
Output:
| NO |
------
| 3 |
SQLFiddle
Try this query
SELECT
*
FROM
(SELECT
#rid:=#rid+1 as rId,
a.*
FROM
tbl a
JOIN
(SELECT #rid:=0) b
ORDER BY
id DESC)tmp
WHERE rId=2;
SQL FIDDLE:
| RID | ID | TYPE | DETAILS |
------------------------------------
| 2 | 28 | Twitter | #sqlfiddle5 |
Another approach
select a.* from supportContacts a inner join
(select max(id) as id
from supportContacts
where
id in (select id from supportContacts where id not in
(select max(id) from supportContacts)))b
on a.id=b.id
SQL FIDDLE:
| ID | TYPE | DETAILS |
------------------------------
| 28 | Twitter | #sqlfiddle5 |
Alternatively, this query will always get the second highest number based on the inner where clause.
SELECT *
FROM
(
SELECT t.col,
(
SELECT COUNT(distinct t2.col)
FROM tableName t2
WHERE t2.col >= t.col
) as rank
FROM tablename t
WHERE col <= 10
) xx
WHERE rank = 2 -- <<== means second highest
SQLFiddle Demo
SQLFiddle Demo (supports duplicate values)
If you want to get next lower number from table
you can get it with this query:
SELECT distinct col FROM table1 a
WHERE 2 = (SELECT count(DISTINCT(b.col)) FROM table1 b WHERE a.col >= b.col);
later again if you want to get third lower number you can just pass 3 in place of 2 in where clause
again if you want to get second higher number, just change the condition of where clause in inner query with
a.col <= b.col