$construct = "SELECT * FROM mytable GROUP BY nid HAVING nid>1";
mytable:
+----+----------+
| id | nid |
+----+----------+
| 1 | null |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 2 |
| 6 | 2 |
| 7 | 3 |
| 8 | 3 |
| 9 | 4 |
| 10 | 4 |
-----------------
How do i GROUP BY nid except nid=1? This is a brief example but with my code i am not getting the desired results. Is the query correct for what i am trying to accomplish?
How about this:
SELECT * FROM mytable WHERE nid != 1 ORDER BY nid
GROUP BY causes an aggregate query which you can only sensibly use with an aggregation function. For example, SELECT COUNT(*), nid GROUP BY nid would give you the counts of rows with a given nid.
Update: Not sure I'm understanding you, but how about this then:
(SELECT * FROM mytable WHERE nid = 1 UNION SELECT * FROM mytable WHERE nid != 1 GROUP BY nid) ORDER BY nid
I'm not sure it makes sense to mix aggregate and non-aggregate queries, though -- on the aggregate side you'll just end up with an indeterminate representative row of that group.
SELECT count(*), nid FROM mytable where nid <> 1 GROUP BY nid;
or
SELECT count(*), nid FROM mytable where nid != 1 GROUP BY nid;
Not sure if you are using Oracle or MySQL.
…
GROUP BY CASE nid WHEN 1 THEN -id ELSE nid END
…
Related
I have the following table:
+----+-----------+-----------+
| id | teacherId | studentId |
+----+-----------+-----------+
| 1 | 1 | 4 |
| 2 | 1 | 2 |
| 3 | 1 | 1 |
| 4 | 1 | 3 |
| 5 | 2 | 2 |
| 6 | 2 | 1 |
| 7 | 2 | 3 |
| 8 | 3 | 9 |
| 9 | 3 | 6 |
| 10 | 1 | 6 |
+----+-----------+-----------+
I need a query to find two teacherId's with maximum number of common studentId's.
In this case teachers with teacherIds 1,2 have common students with studentIds 2, 1, 3, which is greater than 1,3 having common students 6.
Thanks in Advance!
[Edit]: After several hours I've had the following solution:
SELECT * FROM (
SELECT r1tid, r2tid, COUNT(r2tid) AS cnt
FROM (
SELECT r1.teacherId AS r1tid, r2.teacherId AS r2tid
FROM table r1
INNER JOIN table r2 ON r1.studentId=r2.studentId AND r1.teacherId!=r2.teacherId
ORDER BY r1tid
) t
GROUP BY r1tid, r2tid
ORDER BY cnt DESC
) t GROUP BY cnt ORDER BY cnt DESC LIMIT 1;
I was sure that there must exist more short and elegant solution, but I could not find it.
You would do this with a self-join. Assuming no duplicates in the table:
select t.teacherid, t2.teacherid, count(*) as NumStudentsInCommon
from table t join
table t2
on t.studentid = t2.studentid and
t.teacherid < t2.teacherid
group by t.teacherid, t2.teacherid
order by NumStudentsInCommon desc
limit 1;
If you had duplicates, you would just replace count(*) with count(distinct studentid), but count(distinct) requires a bit more work.
select t.teacherId, t2.teacherId, sum(t.studentId) as NumStudentsInCommon
from table1 t join
table1 t2
on t.studentId = t2.studentId and
t.teacherId < t2.teacherId
group by t.teacherId, t2.teacherId
order by NumStudentsInCommon desc
EDIT: The goal is to order the output so that the first result is the row with store_id = 5 and the maximum rank of all items with store_id = 5. Then the rest of the deals are ranked according to their rank in descending order, regardless of their store_id. For the actual query, a union would be too expensive.
An example of data would be:
+----+----------+------+
| id | store_id | rank |
+----+----------+------+
| 1 | 1 | 10 |
+----+----------+------+
| 2 | 5 | 9 |
+----+----------+------+
| 3 | 4 | 8 |
+----+----------+------+
| 4 | 3 | 7 |
+----+----------+------+
| 5 | 3 | 6 |
+----+----------+------+
| 6 | 1 | 5 |
+----+----------+------+
The final query being run would be:
SELECT id,store_ID,IF(#id=id,rank=rank*9999999,rank) AS rank
FROM (
SELECT * FROM (
SELECT id,rank,store_id
FROM items
) b
JOIN (#id:=(SELECT id FROM b WHERE store_id = 5 ORDER BY rank DESC LIMIT 1)) AS s
)
ORDER BY rank DESC
And the desired outcome would be:
+----+----------+------+
| id | store_id | rank |
+----+----------+------+
| 2 | 5 | 9 |
+----+----------+------+
| 1 | 1 | 10 |
+----+----------+------+
| 3 | 4 | 8 |
+----+----------+------+
| 4 | 3 | 7 |
+----+----------+------+
| 5 | 3 | 6 |
+----+----------+------+
| 6 | 1 | 5 |
+----+----------+------+
Here (#id:=(SELECT id FROM b WHERE store_id = 5 ORDER BY rank DESC LIMIT 1)) is being used to select the top scoring item with store_id = 5. This item is meant to appear first in the list, hence why it is reranked by multiplying it by a very large number. All other ranks remain untouched and are ranked according to their score in descending order.
The problem is that the select statement in the join is trying to join a selection from a subquery called b. However, table b does not actually exist in the database, so MySQL is saying that table b does not exist. How can I reference the subquery? The actual subquery is incredibly complex, so I haven't included it here, but if interested you can see a version of it here: MySQL - How To Optimize thie Query. The query in this question is just to keep the problem simple.
You can adjust your order by clause to put a specific row first. Let's say you wanted store_id 5 to be first, you can do this:
ORDER BY store_id = 5 DESC, rank DESC;
Which will put the row with store_id 5 on top. In this example, you want the row that has the highest rank for store_id 5, so I wrote the following subquery:
SELECT id
FROM myTable
WHERE store_id = 5
ORDER BY rank DESC
LIMIT 1;
And I put that in the ORDER BY clause:
SELECT *
FROM myTable
ORDER BY id =
(SELECT id FROM myTable WHERE store_id = 5 ORDER BY rank DESC LIMIT 1) DESC,
rank DESC;
Here is an SQL Fiddle.
EDIT
If you put a subquery in your FROM clause, you won't be able to reference its alias in the subquery of the ORDER BY clause. However, in a very unreadable fashion, you can put the same subquery there to accomplish it, like this:
SELECT *
FROM (SELECT * FROM myTable) b
ORDER BY id =
(SELECT id FROM (SELECT * FROM myTable) b WHERE store_id = 5 ORDER BY rank DESC LIMIT 1) DESC,
rank DESC;
Another Fiddle.
Say if I have a table similar to this but including more columns and more rows (These are the only relevant ones):
+-------+----+
| name | id |
+-------+----+
| james | 1 |
| james | 2 |
| james | 3 |
| adam | 4 |
| max | 5 |
| adam | 6 |
| max | 7 |
| adam | 8 |
+-------+----+
How could I get it so that it would only show the max(id) from each name like:
+-------+----+
| name | id |
+-------+----+
| adam | 8 |
| max | 7 |
| james | 3 |
+-------+----+
I currently just have this
"select * from table order by id desc"
but this just shows the latest ids. I only want to be able to see one of each name.
So basically show only the highest id of each name
You would use aggregation and max():
select name, max(id)
from table t
group by name
order by max(id) desc
limit 40;
EDIT:
If you need select * with the highest id, then use the not exists approach:
select *
from table t
where not exists (select 1 from table t2 where t2.name = t.name and t2.id > t.id)
order by id desc
limit 40;
The "not exists" essentially says: "Get me all rows in the table where there is no other row with the same name and a higher id". That is a round-about way of getting the maximum row.
One way to achieve this is to leverage a non-standard GROUP BY extension in MySQL
SELECT *
FROM
(
SELECT *
FROM table1
ORDER BY id DESC
) q
GROUP BY name
-- LIMIT 40
or another way is to grab a max id per name first and then join back to your table to fetch all other columns
SELECT t.*
FROM
(
SELECT MAX(id) id
FROM table1
GROUP BY name
-- LIMIT 40
) q JOIN table1 t
ON q.id = t.id
ORDER BY name;
Output:
| NAME | ID |
|-------|----|
| adam | 8 |
| james | 3 |
| max | 7 |
Here is SQLFiddle demo
I have a table that I'm selecting from in which I only want results for 2 differt column values... Here is what I mean data wise...
some_table
+----+----------+-------------+
| id | some_id | some_column |
+----+----------+-------------+
| 1 | 10 | alpha |
| 2 | 10 | alpha |
| 3 | 10 | alpha |
| 4 | 20 | alpha |
| 5 | 30 | alpha |
+----+----------+-------------+
An example of the type of query I'm running is:
SELECT * FROM some_table WHERE some_column = `alpha`;
How do I modify that select so that it only gives me results for up to 2 diffent some_id's... an example result is:
some_table
+----+----------+-------------+
| id | some_id | some_column |
+----+----------+-------------+
| 1 | 10 | alpha |
| 2 | 10 | alpha |
| 3 | 10 | alpha |
| 4 | 20 | alpha |
+----+----------+-------------+
It would not include id = 5 row because we only grab results for up to 2 different some_id's (10, 20 in this case).
Actually figured it out on my own, just needed to use a JOIN / SELECT DISTINCT combo. Here is the correct query...
SELECT * FROM some_table s1 JOIN (SELECT DISTINCT some_id FROM s1 LIMIT 2) s2 ON s1.some_id = s2.some_id;
Possibly use a subselect to get the first 2 ids, and then inner join that against your table
SELECT a.id, a.some_id, a.some_column
FROM some_table a
INNER JOIN
(
SELECT DISTINCT some_id
FROM some_table
ORDER BY some_id
LIMIT 2
) b
ON a.some_id = b.some_id
I am not sure if this is the optimal solution but it should do the trick:
SET #firstId:=(select distinct some_id from some_table limit 1) ;
SET #secondId:=(select distinct some_id from some_table limit 1,1) ;
SELECT *
FROM some_table
WHERE some_column="alpha"
AND some_id IN (#firstId, #secondId);
Hi I have a table that looks like
-----------------------------------------------------------
| id | group_id | source_id | target_id | sortsequence |
-----------------------------------------------------------
| 2 | 1 | 2 | 4 | 1 |
-----------------------------------------------------------
| 4 | 1 | 20 | 2 | 1 |
-----------------------------------------------------------
| 5 | 1 | 2 | 14 | 1 |
-----------------------------------------------------------
| 7 | 1 | 2 | 7 | 3 |
-----------------------------------------------------------
| 20 | 2 | 20 | 4 | 3 |
-----------------------------------------------------------
| 21 | 2 | 20 | 4 | 1 |
-----------------------------------------------------------
Scenario
There are two scenarios that needs to be handled.
Sortsequence column value should be unique against one source_id and group_id. For example if all the records having group_id = 1 AND source_id = 2 should have sortsequence unique. In above example records having id= and 5 which are having group_id = 1 and source_id = 2 have same sortsequence which is 1. This is faulty record. I need to find out these records.
If group_id and source_id is same. The sortsequence columns value should be continous. There should be no gap. For example in above table records having id = 20, 21 having same group_id and source_id and sortsequence value is 3 and 1. Even this is unique but there is a gap in sortsequence value. I need to also find out these records.
MY So Far Effort
I have written a query
SELECT source_id,`group_id`,GROUP_CONCAT(id) AS children
FROM
table
GROUP BY source_id,
sortsequence,
`group_id`
HAVING COUNT(*) > 1
This query only address the scenario 1. How to handle scenario 2? Is there any way to do it in same query or I have to write other to handle second scenario.
By the way query will be dealing with million of records in table so performance must be very good.
Got answer from Tere J Comments. Following query covers above mentioned both criteria.
SELECT
source_id, `group_id`, GROUP_CONCAT(id) AS faultyIDS
FROM
table
GROUP BY
source_id,group_id
HAVING
COUNT(DISTINCT sortsequence) <> COUNT(sortsequence) OR COUNT(sortsequence) <> MAX(sortsequence) OR MIN(sortsequence) <> 1
May be it can help others.
Try this query it will solve both of the cases as you have mentioned in the question.
SELECT
a.*
FROM
tbl a
INNER JOIN
(select
#rn:=IF(#prevG = group_id AND #prevS = source_id, #rn + 1, 1) As rId,
#prevG:=group_id AS group_id,
#prevS:=source_id AS source_id,
id,
sortsequence
FROM
tbl
join
(select #rn:=0, #prevS:=0, #prevG:=0)b
order by group_id, source_id, id) b
ON a.id = b.id AND a.SORTSEQUENCE <> b.RID;
FIDDLE