The multi-part identifier could not be bound (join sub query) - sql

Possible duplicate, but providing no insight to this case:
The multi-part identifier could not be bound
I have a query of the following form:
select l.id, l.foo, r.id, r.foo
from tbl l
inner join storyevents r on l.id = r.id
right join (
select distinct foo from tbl where id= l.id
) tmp on l.foo = tmp.foo
where l.foo = 12345
But i get the following error:
The multi-part identifier "l.id" could not be bound.
in relation to the right join sub query.
Bonus points:
This is an attempt to remove duplicate rows from the inner join based on a single column. Better way to do this?

try this,
SELECT l.id, l.foo, r.id, r.foo
FROM storyevents l
INNER JOIN storyevents r
ON l.id = r.id
RIGHT JOIN
(
SELECT distinct extid, foo
FROM storyevents
) tmp on l.foo = tmp.foo AND
tmp.extid = l.id
where l.foo = 12345

Better way to remove duplicate rows?
select distinct l.id, l.foo, r.id, r.foo
....

Related

How to put a CTE as a subquery

In SQL Server, I have a CTE and a select statement.
The CTE looks like
WITH Recursion AS (
SELECT Value, GroupID, Type
FROM #GroupMembership
WHERE GroupID = #requestingGroupID
UNION ALL
SELECT M.Value, M.GroupID, M.Type
FROM Recursion AS R
INNER JOIN #GroupMembership AS M ON R.Value = M.GroupID AND R.Type = 'Group'
)
SELECT * FROM Recursion
and the select statement is this
select *
from [Right] r
inner join Group on r.groupid = Group.id
inner join (
-- CTE but #requestingGroupID should be Group.id
) C on C.GroupID = Group.id
I want to nest the CTE inside the brackets, but the #requestingGroupID from the CTE should be referencing Group.id from outside the bracket.
When I try to do this, I get syntax errors. Does anyone know how to do it?
Thanks
Without sample data and expected results, it's hard to give a definitive answer.
But basically, you cannot use a recursive CTE as an APPLY (where you push outer references in).
The only way to do this is to put the recursive part into an inline Table Valued Function, then APPLY it to the rest of the query
CREATE OR ALTER FUNCTION GetDescendants (#requestingGroupID int)
RETURNS TABLE AS RETURN
WITH Recursion AS (
SELECT Value, GroupID, Type
FROM GroupMembership
WHERE GroupID = #requestingGroupID
UNION ALL
SELECT M.Value, M.GroupID, M.Type
FROM Recursion AS R
INNER JOIN GroupMembership AS M ON R.Value = M.GroupID AND R.Type = 'Group'
)
SELECT *
FROM Recursion;
select *
from [Right] r
inner join Group g on r.groupid = g.id
CROSS APPLY dbo.GetDescendant ( g.id) C
db<>fiddle
Alternatively, you can re-arrange your query so that the main SELECT is in the anchor part (first part) of the recursion. Admittedly this is not always possible.
WITH Recursion AS (
SELECT r.Id, Value, GroupID, Type
FROM [Right] r
inner join Group g on r.groupid = g.id
JOIN #GroupMembership gm ON gm.GroupID = g.GroupID
UNION ALL
SELECT R.Id, M.Value, M.GroupID, M.Type
FROM Recursion AS R
INNER JOIN #GroupMembership AS M ON R.Value = M.GroupID AND R.Type = 'Group'
)
SELECT *
FROM Recursion
db<>fiddle

How to get distinct id in subquery from fact table

Trying to return all the fields in my sub query minus the duplicated childid's in the fact table cor.score. I don't want the duplicate id's to inflate my count. Every id needs to be counted once.
select distinct cs.childid
from
(select s.sitename, c.primarylanguage, count(Primarylanguage) as 'Count'
from cor.scores cs
left join cor.sites s on s.id = cs.siteid
left join cor.children c on c.id = cs.childid
group by s.sitename, c.primarylanguage)
Error:
Msg 102, Level 15, State 1, Line 7 Incorrect syntax near ')'.
What's the best way to go about this?
select distinct cs_childid
from
(
select s.sitename,cs.childid as cs_childid, c.primarylanguage, count(Primarylanguage) as 'Count'
from cor.scores cs
left join cor.sites s on s.id = cs.siteid
left join cor.children c on c.id = cs.childid
group by s.sitename, c.primarylanguage,cs.childid
)as T
Do you want this:
1. can return any column of subquery
select s.childid,s.[Count]
FROM (
SELECT s.sitename, c.primarylanguage,cs.childid, count(Primarylanguage)OVER(PARTITION BY s.sitename) as 'Count'
FROM cor.scores cs
LEFT join cor.sites s on s.id = cs.siteid
) AS s LEFT join cor.children c on c.id = s.childid
Or this:
2. Only can return group column and aggregate columns
select s.childid,s.[Count]
FROM (
SELECT s.sitename,cs.childid, count(Primarylanguage)OVER(PARTITION BY s.sitename) as 'Count'
FROM cor.scores cs
LEFT join cor.sites s on s.id = cs.siteid
GROUP BY s.sitename,cs.childid
) AS s LEFT join cor.children c on c.id = s.childid

Take additional field value

I have this query:
Select
p.Id, p.Nazwa
From
tbProjekt p
Where
EXISTS (select UP.*
from tbUserProject UP
where UP.ProjectId = p.Id And UP.UserId = 1)
I want to select the additional column UP.IsFullAccessso so change the first line to this one:
Select
p.Id, p.Nazwa, UP.IsFullAccess
but I get an error:
The multi-part identifier "UP.IsFullAccess" could not be bound.
You need to join to tbUserProject in your main query then:
Select p.Id, p.Nazwa, UP.IsFullAccess
From tbProjekt p
INNER JOIN tbUserProject UP
ON p.Id = UP.ProjectId
WHERE UP.UserId = 1
UP exists only within the EXISTS sub-query and can't be accessed from the main query. You may be able to use a JOIN instead:
SELECT
p.Id,
p.Nazwa,
UP.IsFullAccess
FROM tbProjekt p
JOIN tbUserProject UP
ON UP.ProjectId = p.Id
AND UP.UserId = 1
The main difference is the possibility of duplicates if you have more than one matching record in the tbUserProject table.
If you need unique values from tbProject and there is many values in tbUserProject for one project and one user use this:
SELECT
p.Id,
p.Nazwa,
UP.IsFullAccess
FROM tbProjekt p
INNER JOIN
(
select ProjectId, MAX(IsFullAccess) as IsFullAccess from tbUserProject UP where UP.UserId = 1
group by ProjectId
) UP ON UP.ProjectId = p.Id

select between two many-to-many relations

I have four tables Level, Tag, Level_Tag and Tag_hierarchy. How can select all tags of a level which have this condition id_tag = id_parent which means the Tag is the root. I can select from join table (Maybe not a good performance?) but I don't know how to add the other self join here.
SELECT level.name, tag.id, tag.name
FROM level INNER JOIN
tag_level ON level.id = tag_level.id_level INNER JOIN
tag ON tag_level.id_tag = tag.id
WHERE (level.Id = #id)
Tag Table contains thousands of rows and I'm really worry about memory and performance issues.
Could you please help me on this? Here is the schema
Try this:
;with cte as
(select id_tag
from tag_hierarchy where id_tag = id_parent)
select l.name, t.id, t.name
from cte c
inner join tag t on t.id = c.id_tag
inner join tag_level tl on t.id = tl.id_tag
inner join level l on tl.id_level = l.id
where l.lid = #id
Maybe you can add another exists. Like this:
SELECT
level.name,
tag.id,
tag.name
FROM
level
INNER JOIN tag_level
ON level.id = tag_level.id_level
INNER JOIN tag
ON tag_level.id_tag = tag.id
WHERE
(level.Id = #id)
AND EXISTS
(
SELECT NULL
FROM Tag_hierarchy
WHERE Tag_hierarchy.id_tag=tag.id
AND Tag_hierarchy.id_tag=Tag_hierarchy.id_parent
)

How to return multiple values when using SELECT EXISTS in postgresql

I have the following SQL query:
SELECT EXISTS (SELECT r.id FROM Rules r INNER JOIN rule_t c on c.id=r.rule_t.id
INNER JOIN user u on u.id = r.user_id
WHERE u.fmnum='2813'
AND c.name='default') ::int
Is there a way I can modify this so that I get two values back, the INT from the EXISTS method, and r.id?
I know that I can change the query so that I remove the EXISTS method... if the sub select returns anything at all, then I know the record exists... but I'm just wondering if its possible to do the above.
Thanks.
EDIT 1
I'm testing the following code in a new query window in pgadmin3...
SELECT *
FROM (
SELECT TRUE, r.id
FROM rules r
JOIN rule_t c on c.id = r.rule_t.id
JOIN user u on u.id = r.user_id
WHERE u.fmnum = '2813'
AND c.name = 'default'
);
But I'm getting the following error:
ERROR: subquery in FROM must have an alias LINE 2: (
^ HINT: For example, FROM (SELECT ...) [AS] foo.
EDIT 2
SELECT *
FROM (
SELECT TRUE, r.id
FROM rules r
JOIN rule_t c on c.id = r.rule_t.id
JOIN user u on u.id = r.user_id
WHERE u.fmnum = '2813'
AND c.name = 'default'
) AS x;
SELECT 1 AS does_exist, r.id
FROM rules r
JOIN rule_t c on c.id = r.rule_t.id
JOIN user u on u.id = r.user_id
WHERE u.fmnum = '2813'
AND c.name = 'default'
LIMIT 1; -- may or may not be needed.
This does what you seem to be asking for: you get two columns. But you get no row if nothing is found.
If you want a row, even if nothing is found, you need a subquery:
SELECT sub.t_id IS NOT NULL AS does_exist, sub.id
FROM (SELECT 1) x -- dummy to guarantee 1 row
LEFT JOIN ( -- LEFT JOIN is crucial
SELECT r.id
FROM rules r
JOIN rule_t c on c.id = r.rule_t.id
JOIN user u on u.id = r.user_id
WHERE u.fmnum = '2813'
AND c.name = 'default'
LIMIT 1 -- may or may not be needed.
) ON TRUE; -- join condition is always true
Or, simpler / faster:
SELECT 1 AS does_exist, r.id
FROM rules r
JOIN rule_t c on c.id = r.rule_t.id
JOIN user u on u.id = r.user_id
WHERE u.fmnum = '2813'
AND c.name = 'default'
UNION ALL
SELECT 0, NULL
LIMIT 1;