Where not in inner join function - sql

So I'm trying to fetch data from my database with an inner join function and I'm trying to use where not on it.
This is my query:
SELECT DISTINCT a.*
FROM students AS a
INNER JOIN messages AS b ON a.id = b.from_id OR b.to_id
WHERE (from_id = a.id AND to_id = 1)
OR (from_id = 1 AND to_id = a.id)
AND a.status = 1
AND NOT a.id = 30
It works fine but the row with id = 30 on students table is still getting selected

Here is the query you need:
You need to change your inner join condition to ON a.id = b.from_id OR a.id = b.to_id
You need to add new parentheses for first where condition like this: WHERE ((from_id = a.id AND to_id = 1) OR (from_id = 1 AND to_id = a.id))
SELECT DISTINCT a.*
FROM students a
INNER JOIN messages b ON a.id = b.from_id OR a.id = b.to_id
WHERE ((from_id = a.id AND to_id = 1) OR (from_id = 1 AND to_id = a.id))
AND a.status = 1
AND not a.id = 30
And here is a small demo on SQLServer(maybe that is your database):
DEMO

Try to add additional parentheses to group 'OR' condition
WHERE ((from_id = a.id AND to_id = 1) OR (from_id = 1 AND to_id = a.id))
AND a.status = 1
AND NOT a.id = 30

SELECT DISTINCT a.* FROM students AS a
INNER JOIN messages AS b
(ON a.id = b.from_id)
OR
(ON a.id = b.to_id)
WHERE
(from_id = a.id AND to_id = 1)
OR (from_id = 1 AND to_id = a.id)
AND a.status = 1
AND NOT a.id = 30

You should add proper () around the OR condition in join and in where
SELECT DISTINCT a.*
FROM students AS a
INNER JOIN messages AS b ON a.id = ( b.from_id OR b.to_id )
WHERE ((b.from_id = a.id AND b.to_id = 1) OR (from_id = 1 AND to_id = a.id) )
AND a.status = 1
AND NOT a.id = 30

SELECT DISTINCT incurs overhead to remove duplicates. That is unnecessary if you use EXISTS:
SELECT s.*
FROM students s
WHERE EXISTS (SELECT 1
FROM messages m
WHERE ( (s.id = m.from_id AND m.to_id = 1) OR
(s.id = m.to_id AND m.from_id = 1)
) AND
m.status = 1 AND
m.id <> 30
);
Of course, the right parentheses are still needed.
Note two other changes:
The table aliases are abbreviations for the table names, rather than arbitrary letters. This makes the query easier to understand, modify, and maintain.
<> is the SQL standard operator for "not equals". Of course, NOT m.id = 30 works, but it is not colloquial.

Related

ORA-01427: single-row subquery returns more than one row error | But I want multiple values

I've below query, into which I'm creating using a subquery which is returning
ORA-01427: single-row subquery returns more than one-row error.
But I want all the values that subquery is returning, and there is no other column left for the join condition. Below is my sample query.
select name,
dob,
cdate,
(select value
from item a,
books b
where a.id = b.id
and a.newid = b.newid
and a.id = s.id
and a.bid = s.cid
and a.eventid=1) col_value,
(select value2
from item a,
books b
where a.id = b.id
and a.newid = b.newid
and a.id = s.id
and a.bid = s.cid
and a.eventid=1) col_value2
from sample s,
purchase p
where s.id = p.id
and s.cid = p.cid
Desired Output
Do I need to apply a Group By? Please let me know your suggestions.
It is little bit more difficult without data but try:
select name -- please use table alias so you know which table the value is from (s, p or cv)
, dob
, cdate
, cv.value
from sample s
left join purchase p on s.id=p.id and s.cid=p.cid --or just join
left join (select value, a.id, a.bid --or just join
from item a
left join books b --or just join
on a.id=b.id and a.newid=b.newid) cv
on cv.id = s.id and cv.bid = s.cid
left join (select value2
from item a
left join books b
on a.id = b.id and a.newid = b.newid) cv2
on cv2.id = s.id and cv2.bid = s.cid
where cv2.eventid=1;
select name,
dob,
cdate,
(select value
from
books b
where a.id = b.id
and a.newid = b.newid ) col_value,
(select value2
from
books b
where a.id = b.id
and a.newid = b.newid ) col_value2
from sample s,
purchase p,item a
where s.id = p.id
and s.cid = p.cid
and a.id(+)=s.id
and a.bid(+)=s.cid
and a.eventid(+)=1

Join to only work if a record present in SQL

I have 2 tables - table1 and table2.
In table2, there are multiple records matching criteria for table1 based on:
c_type, h_level, loop, e_id
I want records from RIGHT table ONLY if there is EXACT 1 match. If not, element_nm should be NULL, so I have the exact same records in the output as left table.
SELECT a.*,
b.element_nm
FROM table1 a
LEFT JOIN table2 b ON
a.c_type = b.c_type
AND a.h_level = b.h_level
AND a.loop = b.loop
AND a.e_id = b.e_id
ORDER BY a.file_name,
a.line_num asc;
As this is about one value only, you can use a Sub query in the select clause. Otherwise you'd use a subquery in a LEFT OUTER JOIN or use OUTER APPLY.
SELECT
t1.*,
(
SELECT MIN(t2.element_nm)
FROM table2 t2
WHERE t2.c_type = t1.c_type
AND t2.h_level = t1.h_level
AND t2.loop = t1.loop
AND t2.e_id = t1.e_id
HAVING COUNT(*) = 1
) AS element_nm
FROM table1 t1
ORDER BY t1.file_name, t1.line_num;
Thorsten's answer works when you want only one column from the second table. But if you want multiple columns, it is a bit cumbersome.
Alternatively:
SELECT a.*, b.*
FROM table1 a LEFT JOIN
(SELECT b.*,
COUNT(*) OVER (PARTITION BY b.c_type, b.h_level, b.loop, b.e_id) as cnt
FROM b
) b
ON a.c_type = b.c_type AND
a.h_level = b.h_level AND
a.loop = b.loop AND
a.e_id = b.e_id AND
b.cnt = 1
ORDER BY a.file_name, a.line_num asc;
you should use Row_Number , like this :
WITH cte AS (
SELECT ROW_NUMBER() OVER(PARTITION BY a.c_type ,a.h_level,a.loop ,a.e_id ) rnum
,a.c_type ,a.h_level,a.loop ,a.e_id FROM table1 a
LEFT JOIN table2 b ON a.c_type = b.c_type AND a.h_level = b.h_level AND a.loop = b.loop AND a.e_id = b.e_id
)
,cte2 AS (SELECT * FROM cte WHERE rnum = 2)
SELECT a.*,
CASE WHEN cte2.element_nm IS NULL then b.element_nm ELSE NULL END element_nm
FROM table1 a
LEFT JOIN table2 b ON a.c_type = b.c_type AND a.h_level = b.h_level AND a.loop = b.loop AND a.e_id = b.e_id
LEFT JOIN cte2 ON a.c_type = cte2.c_type AND a.h_level = cte2.h_level AND a.loop = cte2.loop AND a.e_id = cte2.e_id

SQL Server - Inner Join On - Conditional Statement

I am using SQL Server 2008 and have the following scenario:
I have Table a with fields id and groupId.
I have Table b with fields id and groupId also.
The rule in Table b, is that:
If a.id = 0, then a.groupId = b.groupId
Else a.id = b.id (in which case a.groupId = 0)
The 2 tables are also linked by agrId such that a.agrId = b.agrId
How can I join these tables, whilst satisfying the rule above?
Update: Apologies for the lack of clarity, I have updated the rule and added my attempt below:
select * from a
inner join b
on a.agrId = b.agrId
where (
(b.id > '0' and b.groupId = '0')
or
(b.groupId > '0' and b.id = '0')
)
Try this
SELECT *
FROM TableA A
INNER JOIN TableB B
ON (B.ID = 0 AND A.groupId = B.groupId)
OR (B.groupId = 0 AND A.id = B.id)
Al it needs is an expression that evaluates to true or false, to indicate if these two rows should be joined. So based on what you say, this would be the join condition:
select *
from tA a
inner join tB b
on (a.id = 0 and a.groupid = b.groupid)
or (a.groupid = 0 and a.id = b.id)
Try this:
SELECT *
FROM a INNER JOIN
b ON a.agrId = b.agrId
WHERE (b.id = '0' AND b.groupId = a.groupId)
or
(b.groupId = '0' AND b.id = b.id)

how to update table from select statement in sql server

How can i update a table from select statement results. Here is my select statement:
SELECT count(distinct r.[ID])as Total
FROM Table1 r left join
Tabel2 a
on r.ID = a.ID
where a.Status is null
and here is like what i want to do and i know it is wrong:
update MyTable
set mycol = total
from
(
SELECT count(distinct r.[ID])as Total
FROM Table1 r left join
Tabel2 a
on r.ID = a.ID
where a.Status is null)
Use the subquery in the set:
update MyTable
set mycol = (
SELECT count(distinct r.[ID])
FROM Table1 r left join
Tabel2 a
on r.ID = a.ID
where a.Status is null
)
All you have to do is make a couple tiny changes. Below is the code you will need to use:
update MyTable
set mycol = (SELECT count(distinct r.[ID])as Total
FROM Table1 r left join
Tabel2 a
on r.ID = a.ID
where a.Status is null)
In the case that, as I assume, you have multiple rows in both tables and you want to update the first table row-by-row with related results from your subquery, you'll want to add a join (assuming that both datasets will have what i am calling 'identifyingfield' below) :
Update MyTable
set mycol = b.total
from
MyTable a
inner join
(
SELECT identifyingfield, count(distinct r.[ID])
FROM Table1 r left join
Tabel2 a
on r.ID = a.ID
where a.Status is null
group by identifyingfield
) b
ON a.identifyingfield = b.identifyingfield
you may try something like this:
with "sums"
as
(
select
F."id"
, "sum" = sum( F."value" ) over ( partition by F."id" )
from
"foo" F
)
update
B
set
B."totals" = S."sum"
from
"bar" B
inner join "sums" S
on S."id" = B."id";
see sql-fiddle here

Does not Recognize Column in Where Clause when Joining Tables

SELECT * FROM a
JOIN (SELECT * FROM b WHERE b.aId = a.Id) AS c ON c.aId = a.Id
It says does not recognize: a.Id in the Where Clause.
I know its probably cause im using a temp table and a.Id cannot be passed through but is there any way we can do that?
Because here is what actually happens
SELECT *
FROM a
JOIN (SELECT * FROM b
WHERE b.aId = a.Id
ORDER BY b.dateReg DESC
LIMIT 1) AS c ON c.aId = a.Id
I need the ORDER BY b.dateReg DESC LIMIT 1 as it returns me the last row that assosiates with the a Table.. If you require i can post the Create Query
-- find last rows on b
select * from b x
where exists(
select id
from b y
where y.id = b.id
having max(y.dateReg) = x.dateReg
group by id
)
-- then join that b to a, this is the final query:
select * from a
join
(
select * from b x
where exists(
select id
from b y
where y.id = b.id
having max(y.dateReg) = x.dateReg
group by id
)
) as last_rows on last_rows.id = a.id
-- simpler:
select *
from a join b x on a.id = x.id
where exists(
select id
from b y
where y.id = b.id
having max(y.dateReg) = x.dateReg
group by id)
-- or if you will use postgres:
select DISTINCT ON (a.id) *
from a join b x on a.id = x.id
order by a.id, b.dateReg DESC
-- look ma! no group by!
-- nothing beats postgresql's simplicity :-)
Try:
SELECT DISTINCT *
FROM A
JOIN B b ON b.aid = a.id
JOIN (SELECT b.aid,
MAX(b.datereg) 'max_datereg'
FROM B b
GROUP BY b.aid) md ON md.aid = b.aid
AND md.max_datereg = b.datereg
If you do want the first record associated with the associate, use:
SELECT DISTINCT *
FROM A
JOIN B b ON b.aid = a.id
JOIN (SELECT b.aid,
MIN(b.datereg) 'min_datereg'
FROM B b
GROUP BY b.aid) md ON md.aid = b.aid
AND md.min_datereg = b.datereg