Multi-part identifer issue when joining table to function - sql

THE QUESTION:
My join is not working as expected:
join db1.function1(t1.id) f1 on f1.id = t1.id
The error message is:
Incorrect syntax near '.'.
The multi-part identifier 't1.id' could not be found.
This bit (t1.id) is underlined with a red squiggly line.
I get the same issue when I try to use cross apply.
The function is a table-valued function, so returns a table.
FULL QUERY EXAMPLE:
select t1.*, f1.*
FROM db1.dbo.table1 t1
join db1.dbo.function1(t1.id) f1 on f1.id = t1.id
SIMPLIFIED VERSION OF THE FULL FUNCTION:
USE [DB1]
ALTER FUNCTION [dbo].[function1](#id int)
RETURNS #return_table TABLE (
id INT,
col1 INT,
col2 VARCHAR(255)
)
AS
BEGIN
-- ...
-- do all sorts of stuff here to get needed data, temp tables, unions, joins, inserts, selects etc
-- ...
INSERT INTO #return_table (id, col1, col2)
select distinct id, col1,
(
select isnull(cast(col2 as varchar(3)),'NULL')+','
from #tbl2 t2
where t2.id = t1.id
for xml path('')
) col2
from #tbl1 a1
RETURN;
END;

Hi So basically you are looking to perform a INNER JOIN. The problem you are fasing is that you are using a Function. What you should be using to achieve is a CROSS JOIN. If left Join is desired, you could use a OUTER JOIN
There is a great artical that explains in detail that has helped me in the past and can be navigated to by following this link. ClickMe. Search for Use case 5: APPLY and TVFs
I have also written a code that may be of some assistance for the immediate request
Test Data:
IF OBJECT_ID(N'Test1')>0
BEGIN
DROP TABLE Test1
END
CREATE TABLE Test1 (Tbl1ID INT IDENTITY(1, 1),
Name VARCHAR(250))
INSERT INTO Test1
VALUES('ABCD1'),
('ABCD2'),
('ABCD3'),
('ABCD4'),
('ABCD5'),
('ABCD6')
IF OBJECT_ID(N'Test2')>0
BEGIN
DROP TABLE Test2
END
CREATE TABLE Test2 (Tbl2ID INT IDENTITY(1, 1),
Name VARCHAR(250))
INSERT INTO Test2
VALUES('ABCD1'),
('ABCD2'),
('ABCD4'),
('ABCD5')
GO
TV Function:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ufn_TVFTable2]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[ufn_TVFTable2]
GO
CREATE FUNCTION ufn_TVFTable2 (#Name NVARCHAR(1000))
RETURNS TABLE
AS
RETURN
SELECT *
FROM Test2
WHERE Name = #Name
GO
Usage:
SELECT t.*,tf.*
FROM Test1 AS T
CROSS APPLY dbo.ufn_TVFTable2(T.name) AS tf
Cleanup:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ufn_TVFTable2]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[ufn_TVFTable2]
GO
IF OBJECT_ID(N'Test1')>0
BEGIN
DROP TABLE Test1
END
IF OBJECT_ID(N'Test2')>0
BEGIN
DROP TABLE Test2
END
Your Query would look like below:
SELECT t1.*,
f1.*
FROM db1.dbo.table1 AS t1
CROSS APPLY db1.dbo.function1(t1.id) AS f1
Usage Example 2:
;WITH cte_table1(ID) AS
(
SELECT 82938382 AS ID UNION ALL
SELECT 82938383 AS ID UNION ALL
SELECT 82938384 AS ID UNION ALL
SELECT 82938385 AS ID
)
SELECT t1.*,
f1.*
FROM db1.dbo.table1 AS t1
CROSS APPLY db1.dbo.function1(t1.id) AS f1

You are trying to join onto a function using a column from table1 before the join has actually happened. Think of the return of function1 as an atomic operation which you are then joining to table1. So at that point, SQL doesn't know what the value of the id column is.
Assuming function1 does not return a table, you probably wanted to do this:
SELECT t1.*, db1.dbo.function1(t1.id)
FROM db1.dbo.table1 t1

Related

Function to multiple tables

I have this function, but I wanted to pass a table so as to use the same function to get the job done for multiple tables. For example, I want this function work for table1, and table2. But it is just for table1 currently. I was trying to use a dynamic sql in vain; it doesn't pass the parameter selected.
Can someone help? Give me guide on how to pass table as a parameter.
Sample data, table1
CREATE TABLE table1 (id int identity (1,1), name varchar(60))
INSERT INTO table1
VALUES ('a1, a2, a9, a8')
Sample data, table2
CREATE TABLE table2 (id int identity (1,1), name varchar(60))
INSERT INTO table2
VALUES ('a1, a2, a9, a8')
The function:
CREATE FUNCTION f_split
(#id INT)
RETURNS #ab
TABLE (name VARCHAR(20),
ab1 VARCHAR(5)
)
AS
BEGIN
DECLARE #temp TABLE (rn INT, name VARCHAR(5))
INSERT INTO #temp(rn, name)
SELECT ROW_NUMBER() OVER(ORDER BY LTRIM(RTRIM(Split.a.value('.', 'NVARCHAR(MAX)'))) ASC) rn, LTRIM(RTRIM(Split.a.value('.', 'NVARCHAR(MAX)'))) Result
FROM
(
SELECT CAST('<X>'+REPLACE([name], ',', '</X><X>')+'</X>' AS XML) AS String
FROM table1 where id = #id
) AS A
CROSS APPLY String.nodes('/X') AS Split(a)
ORDER BY 1
INSERT INTO #ab
SELECT * FROM #temp
RETURN
END
This gives the result from table1.
SELECT * FROM F_SPLIT(1)
But I want the same function to work for table2 as well.
Any help is appreciated.
Use a partitioned view, which will allow you to specify the table name as a parameter in the where clause.
Start by creating a view that unions the two tables, plus an additional column to indicate which table the row comes from.
CREATE VIEW BothTables AS
SELECT 'Table1' TableName, * FROM Table1
UNION ALL
SELECT 'Table2' TableName, * FROM Table2
Then modify your function. When you pass the table name, use it to select a subset of rows from the view. So instead of
SELECT CAST('<X>'+REPLACE([name], ',', '</X><X>')+'</X>' AS XML) AS String
FROM table1
WHERE id = #id
Use
SELECT CAST('<X>'+REPLACE([name], ',', '</X><X>')+'</X>' AS XML) AS String
FROM BothTables
WHERE TableName = #TableName
AND id = #id

SQL Server hierarchy referencing and cross data referencing

This might be a stupid question, but I am not a DBA and kind of stuck with this issue. I have an application that trickles down all effects (asdf) under an applied ID (IDParent).
The data tables are setup like this:
Data Tables
3rd Data Table
I want to write a query that when using IDChild it will reference that entry's IDParent to get the parent ID while referencing it as an IDChild. For example for the data entry starting at 116 I want to use the parent ID (124) and get 321 in T1. I want to use this to get the RandoName associated with RandoID for all of the entries that has a parent ID of 321.
Right now I am using a script something like:
Select t.[NAME]
From T2 tv
Inner join T3 t on t.RandoID = tv.RandoId
Where
tv.IDChild = T1.IDChild OR tv.IDChild = T1.IDParent
but I'm not sure how to get the whole applied hierarchy.
This would yield something like this:
Resulting Query
PS. I can not change the tables/db schema. But maybe I can add one to do all the referencing? Please tell me what you think.
EDIT I'm sorry I forgot about this other stupid table that RandoID uses which contains the name of the RandoID. I am trying to get the name of RandoID
I think a loop could help you.
Try this:
CREATE TABLE #t1 (IDChild Int, IDParent Int);
CREATE TABLE #t2 (RandoID NVARCHAR(10) , IDChild Int);
CREATE TABLE #RandoName (RandoID NVARCHAR(10), RandoName VARCHAR(50));
INSERT INTO #t1 VALUES (321, NULL), (123,321),(124,123),(116,124)
INSERT INTO #t2 VALUES ('asdf', 123)
INSERT INTO #RandoName VALUES ('asdf', 'something')
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 100)) [RowNum], a.IDChild a, a.IDParent b, b.IDChild c INTO #t3 FROM #t1 a
LEFT OUTER JOIN #t1 b ON b.IDParent = a.IDChild
DECLARE #rownum INT;
DECLARE cbcursor CURSOR for Select RowNum FROM #t3;
OPEN cbcursor;
Fetch Next from cbcursor into #rownum
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE #t3
SET c = (SELECT b from #t3 where RowNum = #rownum-1)
WHERE RowNum = #rownum
Fetch Next from cbcursor into #rownum;
END;
Close cbcursor;
Deallocate cbcursor;
SELECT a,b,t2.RandoID, r.RandoName FROM #t3
LEFT OUTER JOIN #t2 t2 on t2.IDChild = #t3.c OR t2.IDChild = #t3.b OR t2.IDChild = #t3.a
LEFT OUTER JOIN #RandoName r on t2.RandoID = r.RandoID
This is what I get:
If you have any changes in your tables, like more records for T2, this script should be modified.
Using recursion:
declare #t table (IDc int , Idp int)
insert into #t
values
(321,null)
,(123,321)
,(124,123)
,(116,124)
declare #t2 table (RandoID varchar(10), IDChild int)
insert into #t2
values('asdf',123)
;with cte as
(
select anchor = IDChild
,ParentOrSelf = IDc
,RandoID
,RandomName
from #t
cross join (select RandoID,RandoName from #t2 t2 join #t3 t3 on t2.RandoID=t3.RandoID) crossed
where IDc=#anchor
union all
select t2.IDChild
,IDc
, t2.RandoID,RandomName
from #t t
cross join (select RandoID,RandoName from #t2 t2 join #t3 t3 on t2.RandoID=t3.RandoID) t2
join cte on cte.ParentOrSelf = t.Idp
)
select IDc
, cte.RandoID,cte.RandomName
from #t t
left join cte on t.IDc = cte.ParentOrSelf
Results:
IDc RandoID
321 NULL
123 asdf
124 asdf
116 asdf

TSQL - The multi-part identifier bounding error

CREATE FUNCTION [dbo].[Test] (#ID INT, #VAL INT)
RETURNS #Return TABLE (ID INT, VAL INT)
AS
BEGIN
INSERT INTO #Return
SELECT #ID, #VAL
RETURN;
END
GO
DECLARE #T1 TABLE (ID INT IDENTITY(1,1), VAL INT)
DECLARE #T2 TABLE (ID INT, VAL INT)
INSERT INTO #T1
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
INSERT INTO #T2
SELECT 1,1
UNION
SELECT 2,4
UNION
SELECT 3,3
SELECT *
FROM #T1 T1
LEFT JOIN #T2 T2 ON T1.[ID] = T2.[ID]
LEFT JOIN [dbo].[Test] (1, COALESCE(T2.[VAL],T1.VAL)) T ON T1.ID = T.ID
GO
DROP FUNCTION [dbo].[Test]
GO
Goal:
To pass in T2.Val into the 2nd param of the fx if available, else pass in T1.Val. Changing the FX definition is not possible.
I can't seem to get this work. I tried ISNULL and that doesn't work either.
If you want to call a table valued function, use APPLY (OUTER APPLY in this case because you are using LEFT JOIN):
SELECT *
FROM #T1 T1 LEFT JOIN
#T2 T2
ON T1.[ID] = T2.[ID] OUTER APPLY
[dbo].[Test](1, COALESCE(T2.[VAL], T1.VAL) ) T;
If you want an additional condition, then use a WHERE clause:
SELECT *
FROM #T1 T1 LEFT JOIN
#T2 T2
ON T1.[ID] = T2.[ID] OUTER APPLY
[dbo].[Test](1, COALESCE(T2.[VAL], T1.VAL) ) T
WHERE t1.ID = T.ID;
That last condition seems strange, though. Why not just pass T1.ID into the function directly?

Why No Error in T-SQL?

When the following SQL is run you don't get an error in either SQL version 2005 or 2008 R2.
select 1 as MyVal, 'string' as MyText
into #table1
select 1 as thisColumnDoesntExistInTable1, 'string' as MyText
into #table2
select * from #table1
select * from #table2
-- WHY NO ERROR HERE ---
select *
from #table2
where thisColumnDoesntExistInTable1 in
(
select thisColumnDoesntExistInTable1 from #table1
)
drop table #table1
drop table #table2
But if you change the statement as follows by adding an alias to the inner select...
select *
from #table2
where thisColumnDoesntExistInTable1 in
(
select a.thisColumnDoesntExistInTable1 from #table1 a
)
...you do get an error.
Effectively, you have this. So no error
select * from #table2 t2
where thisColumnDoesntExistInTable1 in
(select t2.thisColumnDoesntExistInTable1 from #table1 )
When you qualify this to be explicit for table1, you get the error
The scope of the query is available in the subselect. You can see this more clearly if you change what's in #table2.
select 1 as MyVal, 'string' as MyText
into #table1
select 2 as thisColumnDoesntExistInTable1, 'string' as MyText
into #table2
select * from #table1
select * from #table2
select * from #table2 where thisColumnDoesntExistInTable1 in (select thisColumnDoesntExistInTable1 from #table1 )
drop table #table1
drop table #table2
So you can see, the result will show 2 instead of 1 because you're accessing the value of thisColumnDoesntExistInTable1 from #table2.
Column thisColumnDoesntExistInTable1 does not, as it says, exist in #table1. In the first query, when the compiler hits the subquery, since the column is not aliased it looks in all tables involved in the query, finds it in one, and uses it from there. In the second query, the column is aliased, so SQL only checks the referenced table for the column, doesn't find it, and throws the error.

please help me to create multi insert query

I have got two table
create table t1(cid int, isnews int)
create table t2(nid int,cid int, isnews int)
situations is like this:
if t2 contain t2.cid = t1.cid then the t2.isnews = t1.news and
if t2 not contain cid of t1 then new record should be inserted in t2 and that t1.cid, t1.isnews should be inserted in t2..
and complete table should be done in single query... i have done the updation part but not able to do insertion part..
update query:
UPDATE t22
SET t22.isnews = t11.isnews
FROM t2 AS t22
JOIN t1 AS t11
ON t11.cid= t22.cid
i have prepared below cursor for insert... is it good? :
DECLARE #clntid INT
DECLARE #clntnewsltr INT
DECLARE clientnews CURSOR FOR
SELECT clientid,newsLetter
FROM clients
WHERE clientid NOT IN (SELECT clientid FROM clientprivacy)
OPEN clientnews
FETCH NEXT FROM clientnews INTO #clntid,#clntnewsltr
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO clientprivacy (clientId,tdNewsLetters) VALUES(#clntid, #clntnewsltr)
FETCH NEXT FROM clientnews INTO #clntid,#clntnewsltr
END
CLOSE clientnews
DEALLOCATE clientnews
I think this is the kind of thing you're after:
--INSERT t2 (cid, isnews)
SELECT t1.cid, t1.isnews
FROM t1
LEFT JOIN t2 ON t1.cid = t2.cid
WHERE t2.cid IS NULL
I've commented out the INSERT line - I recommend you run the SELECT on it's own first to check it does give you the correct result (all records from t1 that don't have a matching cid in t2).
I've assumed t2.nid is an IDENTITY column.
You will be so much better off without cursors :) Cursors take MUCH longer to run in large data sets.
It is true you can use a LEFT JOIN, but you can also use a SELECT in your WHERE clause. Most of the time it's a style choice.
CREATE TABLE table1(col_1 int, col_2 int)
CREATE TABLE table2(nid int, col_1 int, col_2 int)
INSERT INTO table2 (col_1,col_2)
SELECT col_1,col_2
FROM table1
WHERE col_1 NOT IN (SELECT col_1 FROM table2)