How to select more rows from subquery - mysql

When I'm trying to run this query:
select * FROM `activity`
WHERE user_id = 1
AND activity_id NOT LIKE (select activity_id from activity where user_id = 1 ORDER BY activity_id DESC LIMIT 8)
I get the follow error:
Subquery returns more than 1 row
How can I solve this problem? I want to select the activity_id from the table excluding the latest 8 activity_id's for a certain user.

NOT LIKE is expecting an expression or a value to compare against and not a resultset.
Change NOT LIKE for NOT IN
Try this one:
SELECT * FROM `activity`
WHERE user_id = 1 AND activity_id NOT IN (
SELECT activity_id FROM activity WHERE user_id = 1
ORDER BY activity_id DESC LIMIT 8)

Solved it by doing this:
$sql2 = "DELETE t1.*
FROM activity t1
left join (select activity_id from activity where user_id = '".$row['user_id']."' ORDER BY activity_id DESC LIMIT 8) t2
on (t1.activity_id = t2.activity_id)
where t2.activity_id is null
and t1.user_id = '".$row['user_id']."'";

Related

Order by With Union SQL

SELECT to_user_id AS sender_id
FROM $chat_table_name
WHERE((to_user_id = $current_user_id OR from_user_id = $current_user_id) and post_id=$post_id)
UNION
SELECT from_user_id AS receiver_id
FROM $chat_table_name
WHERE( (from_user_id = $current_user_id OR to_user_id = $current_user_id) and post_id=$post_id )
order by chat_message_id DESC
How to achieve order by with this query
If I get your question correct, you want to order the indivual SELECTs seperately and you cannot use two ORDER BY clause per each statement instead, add an extra integer column to each SELECT as shown below:
SELECT *, 1 as orderBy from t1
UNION ALL
SELECT *, 2 as orderBy from t2
ORDER BY orderBy desc.

Group by select based on OR condition

After using UNION with two select queries, I'm getting following results
UserId Name Status
------ ------ --------
1 User1 Active
2 User2 Active
1 User1 InActive
3 User3 InActive
But the expected results is
UserId Name Status
---------------------
1 User1 Active
2 User2 Active
3 User3 InActive
Here what I need is, I want to group by column Id and get status as Active if any one result is active. How to form a SQL query for this?
Can anyone suggest query for any one of the following DB?
MSSQL
Oracle
MySQL
PostgreSQL
Edit:
This is the query I've tried in PostgreSQL
(SELECT DISTINCT User.Id,User.DisplayName,AppAccessToUsers.IsActive='1' AND User.IsActive='1' AS IsStatusActive
FROM Applications Left JOIN AppAccessToUsers ON (Applications.Id=AppAccessToUsers.ApplicationId)
Left JOIN User ON (AppAccessToUsers.UserId=User.Id) WHERE Applications.ClientId='e7e66c1b-b3b8-4ffb-844b-fc4840803265')
UNION
(SELECT DISTINCT User.Id,User.DisplayName,AppAccessToGroups.IsActive='1' AND Group.IsActive='1' AND UserGroup.IsActive='1' AND User.IsActive='1' AS IsStatusActive
FROM Applications Left JOIN AppAccessToGroups ON (Applications.Id=AppAccessToGroups.ApplicationId)
Left JOIN Group ON (AppAccessToGroups.GroupId=Group.Id) Left JOIN UserGroup ON (Group.Id=UserGroup.GroupId)
Left JOIN User ON (UserGroup.UserId=User.Id) WHERE Applications.ClientId='e7e66c1b-b3b8-4ffb-844b-fc4840803265')
Use this query,
SELECT UserId
,Name
,CASE WHEN min(status) = 'Active' THEN 'Active' ELSE 'InActive' END
FROM users GROUP BY UserId,Name
I would do the following, assuming a) your tables are called t1 and t2 (amend as appropriate for your actual table names) and b) the names for each userid in both tables are the same - ie. for userid = 1, both tables have the same name:
SELECT userid,
NAME,
MIN(status)
FROM (SELECT userid, NAME, status FROM t1
UNION ALL
SELECT userid, NAME, status FROM t2)
GROUP BY userid, NAME;
This works in Oracle, and I'm pretty sure it'll work in the other database platforms you mentioned.
N.B. I used MIN(status) since you appear to want a status of Active to override a status of Inactive, and A comes before I in the alphabet.
In Sql-server, you could use group by or Row_number like this
DECLARE #SampleData AS TABLE
(
UserId int,
Name varchar(20),
Status varchar(10)
)
INSERT INTO #SampleData
(
UserId,Name,Status
)
VALUES
(1,'User1', 'Active'),
(2,'User2', 'Active'),
(1,'User1', 'InActive'),
(3,'User3', 'InActive')
-- use row_number
;WITH temp AS
(
SELECT *, row_number() OVER(PARTITION BY sd.UserId ORDER BY sd.Status ) AS Rn
FROM #SampleData sd
)
SELECT t.UserId, t.Name, t.Status
FROM temp t WHERE t.Rn = 1
--or use group by
SELECT sd.UserId, sd.Name, min(sd.Status) AS status
FROM #SampleData sd
GROUP BY sd.UserId, sd.Name
Results:
UserId Name Status
1 User1 Active
2 User2 Active
3 User3 InActive
In case of MS Sql Server you can try row_number
;with cte as (
select top 1 with ties * from
( select * from #youruser
union all
select * from #youruser) a
order by row_number() over (partition by userid order by [status] desc)
) select * from cte where status = 'Active'
select your_table.* from your_table
inner join (
select UserId, min(Status) as st from your_table
group by UserId
) t
on your_table.UserId = t.UserId and your_table.Status = t.st
Note: if same UserId can have same Status more than 1 times, then this returns duplicated results.
;With cte (UserId, Name,Status)
AS
(
SELECT 1,'User1','Active' Union all
SELECT 2,'User2','Active' Union all
SELECT 1,'User1','InActive' Union all
SELECT 3,'User3','InActive'
)
SELECT UserId
,NAME
,[Status]
FROM (
SELECT *
,ROW_NUMBER() OVER (
PARTITION BY UserId
,NAME ORDER BY STATUS
) AS Seq
FROM cte
) dt
WHERE dt.Seq = 1
OutPut
UserId Name Status
-----------------------
1 User1 Active
2 User2 Active
3 User3 InActive
for postgres you can use CASE and bool_or, eg:
t=# with a(i,n,b) as (
values (1,'a','active'), (1,'a','inactive'), (2,'b','inactive'), (2,'b','inactive')
)
select i,n,case when bool_or(b = 'active') then 'active' else 'inactive' end
from a
group by i,n
;
i | n | case
---+---+----------
1 | a | active
2 | b | inactive
(2 rows)
Another approach:
Note : Group by is to remove duplicate
select
A.USERID, A.NAME,A.STATUS
from TAB_1 A
LEFT JOIN
(SELECT * FROM TAB_1 WHERE STATUS='Active') B
ON A.USERID=B.USERID
WHERE
( B.STATUS IS NULL OR A.STATUS=B.STATUS)
GROUP BY A.USERID, A.NAME,A.STATUS
ORDER BY A.USERID
;

How to combine two fields value in mysql

This is output
but i want
in this table tb_user_connection i get 4 row but i want only 3 so, if i get connection in 1 and 2 so not get repeated column.
i try below query and this is codeigniter project.
my query:
SELECT * FROM `tb_user_connection` WHERE `connection_type` = 'a' AND (user_id = '1' OR connection_id='1')
You can do this:
select *
from your_table
where user_id <= connection_id
union all
select *
from your_table t
where user_id > connection_id
and not exists (
select 1
from your_table t1
where t1.user_id = t.connection_id
and t1.connection_id = t.user_id
) t1;

update row if count(*) > n

my DB has this structure:
ID | text | time | valid
This is my current code. I'm trying to find a way to do this as one query.
rows = select * from table where ID=x order by time desc;
n=0;
foreach rows{
if(n > 3){
update table set valid = -1 where rows[n];
}
n++
}
I'm checking how many rows exist for a given ID. Then I need to set valid=-1 for all rows where n >3;
Is there a way to do this with one query?
You can use a subquery in the WHERE clause, like this:
UPDATE table
SET valid=-1
WHERE (
SELECT COUNT(*)
FROM table tt
WHERE tt.time > table.time
AND tt.ID = table.ID
) > 3
The subquery counts the rows with the same ID and a later time. This count will be three or less for the three latest rows; the remaining ones would have a greater count, so their valid field would be updated.
Assuming that (id,time) has a UNIQUE constraint, i.e. no two rows have the same id and same time:
UPDATE
tableX AS tu
JOIN
( SELECT time
FROM tableX
WHERE id = #X -- the given ID
ORDER BY time DESC
LIMIT 1 OFFSET 2
) AS t3
ON tu.id = #X -- given ID again
AND tu.time < t3.time
SET
tu.valid = -1 ;
update table
set valid = -1
where id in (select id
from table
where id = GIVEN_ID
group by id
having count(1) >3)
Update: I really like dasblinkenlight's solution because is very neat, but I wanted to try also to do it in my way, a quite verbose one:
update Table1
set valid = -1
where (id, time) in (select id,
time
from (select id,time
from table1
where id in (select id
from table1
group by id
having count(1) >3)
-- and id = GIVEN_ID
order by time
limit 3, 10000000)
t);
Also in SQLFiddle
to do it for all ids, or only for one if you set a where in the a subquery
UPDATE TABLE
LEFT JOIN (
SELECT *
FROM (
SELECT #rn:=if(#prv=id, #rn+1, 1) AS rId,
#prv:=id AS id,
TABLE.*
FROM TABLE
JOIN ( SELECT #prv:=0, #rn:=0 ) tmp
ORDER BY id, TIMESTAMP
) a
WHERE rid > 3
) ordered ON ordered.id = TABLE.id
AND ordered.TIMESTAMP = TABLE.TIMESTAMP
AND ordered.text = TIMESTAMP.text
SET VALID = -1
WHERE rid IS NOT NULL

sql messages table query

this is my conversation table:
conversationID || userID
1 || 1
1 || 2
2 || 1
2 || 2
2 || 3
as you can see each conversation can contain 2 users or more.
I am trying to get the id of the conversation that only 2 users are there.
ie conversation that contains only users 1 & 2, the answer is conversation 1.
but how do I get it?
This will select all conversations that have users 1 or user 2, or both, but no one else:
select conversationID
from conversations
group by conversationID
having count(*) = count(case when userID in (1,2) then 1 end)
If you also want all conversations that have exactly user 1 and 2, and no one else, you also have to add an and condition:
select conversationID
from conversations
group by conversationID
having count(*) = count(case when userID in (1,2) then 1 end)
and count(*) = 2 -- number of elements in set
If userID can be duplicated, it's also better to use distinct:
select conversationID
from conversations
group by conversationID
having
count(distinct userID) = count(distinct case when userID in (1,2) then userID end)
and count(distinct userID) = 2 -- number of elements in set
You should use having clause. Assuming that ( conversationID, userID ) is PK or AK, your query is:
select conversationID
from your_Table
group by conversationID
having count( * ) = 2
Edited Joined with 1,2 user conversations, this is the index friendly approach with out correlate subquery and without function by row.
select t1 conversationID
from your_Table t1
inner join
( select distinct conversationID
from your_Table
where userId in (1, 2)
) t2
on t1.conversationID = t2.conversationID
group by t1.conversationID
having count( distinct t1.userId ) = 2
Hope this helps you,
select conversationID from conversation
group by ConversationID having count(distinct UserID)=2;
sqlfiddle demo