Incorrect syntax near the keyword 'IF' - sql-server

Why do I get "Incorrect syntax near the keyword 'IF'" in the following SQL?:
use AdventureWorks
CREATE FUNCTION Query2_Function(#DPT INT)
RETURNS TABLE
AS
RETURN
IF #DPT is not null
select edh.departmentid,d.name,count(*)as cnt
From HumanResources.Employee e
inner join HumanResources.EmployeeDepartmentHistory edh on e.employeeID = edh.employeeid
inner join humanresources.department d on edh.departmentid = d.departmentid
where d.Name = #dpt
group by edh.departmentid, d.name

You cannot have any flow of control statements in inline table valued functions. If #dpt is null the query will return an empty result set anyway
Edit: or at least would if the correct datatype. You have #DPT INT and are comparing against a name column. That seems doomed to failure at execution time.
Edit 2:
As a solution, you could 1) simply drop the IF #DPT is not null line and 2) either
change the #DPT parameter's type from INT to something like varchar(100), if the function was supposed to search for the department by name,
or
change the WHERE clause to something like this:
where d.departmentid = #dpt
if you meant it to search by department ID.

Try this
CREATE FUNCTION Query2_Function(#DPT INT)
RETURNS #tbl TABLE
(
departmentid int ,
[name] varchar(100),
cnt int
)
AS
begin
IF #DPT is not null
insert into #tbl (departmentid,name,cnt)
select edh.departmentid,d.name,count(*)as cnt
From HumanResources.Employee e
inner join HumanResources.EmployeeDepartmentHistory edh on e.employeeID = edh.employeeid
inner join humanresources.department d on edh.departmentid = d.departmentid
where d.DepartmentID =#DPT
group by edh.departmentid, d.name
return
end
GO

Related

Snowflake replace Not IN query with Not Exist or Left join does not give same count

I am trying to replace not in query with not exist, However it is not giving the correct count.
Current Query
Select count(*)
from employee where
employee.city not in(
Select distinct d.city from
department d, allocation a
where d.id = a.id)
The count it is giving is 348988
Now I need to replace it with exist or left join
Select count(*)
from employee where not exists
(Select 1
from
department d, allocation a
where d.id = a.id and d.city = employee.city);
The count is 410991.. Even doing the left join is giving me the same number.
Please note: we need to replace "not in" Since there is more join after "not in" and snowflake is throwing error - snowflake unsupported subquery type cannot be evaluated. which can be resolved by using not exists
Select count(*)
from employee where
employee.city not in(
Select distinct d.city from
department d, allocation a
where d.id = a.id)
a NOT IN, is the same LEFT JOIN x WHERE x.y IS NUL
Select count(*)
from employee as e
left join (
Select distinct d.city
from department as d
join allocation as a
on d.id = a.id
) as b
on b.city = e.city
where b.city is null
and in fact the DISTINCT can be expensive, and ignored as we are take the join fail thus it should be faster to:
Select count(*)
from employee as e
left join (
Select d.city
from department as d
join allocation as a
on d.id = a.id
) as b
on b.city = e.city
where b.city is null
As suggested by Dean heather it has something to do with NULL variable.
below is the modified query with not exist where the count matches with NOT IN
Select count(*)
from employee where not exists
(Select 1
from
department d, allocation a
where d.id = a.id and d.city = employee.city)
and employee.city is not NULL;
So basically I had to eliminate all the NULL city from primary table .
Although the count is perfectly matching I am now curious if NOT IN was doing the correct job.

Join on Table Variable if it has data

I have a stored procedure that accepts a user defined table type, which is just a list of ints.
If the table is not set (Either sent in as NULL, or has no rows), then my existing query to return data is OK. But I need to only return Ids that are in that table variable.
So if the table variable has data, I would like to INNER JOIN on it, to only return matching Ids.
So that the moment, it's a basic query like this: (Example)
SELECT ...
FROM MyTable
Where UserId = 1
But I need to somehow:
SELECT ...
FROM MyTable m
INNER JOIN #MyTableVariable v ON v.Id = m.Id, --But only if #MyTableVariable has data
Where UserId = 1
Can I do the inner join, only when there's data in #MyTableVriable? Or maybe EXITSTS would help?
Of course, a relatively simple approach (assuming your SELECT statement is not too complicated and you don't mind "duplicating" it) would be:
IF EXISTS (SELECT 1 FROM #MyTableVariable)
BEGIN
SELECT ...
FROM MyTable m
INNER JOIN #MyTableVariable v ON v.Id = m.Id, --But only if #MyTableVariable has data
Where UserId = 1;
END
ELSE
BEGIN
SELECT ...
FROM MyTable m
Where UserId = 1;
END
Otherwise
SELECT ...
FROM MyTable m
LEFT OUTER JOIN #MyTableVariable v ON v.Id = m.Id, --But only if #MyTableVariable has data
Where UserId = 1
AND
(
(EXISTS (SELECT 1 FROM #MyTableVariable)
AND v.Id Is Not NULL)
OR
(NOT EXISTS (SELECT 1 FROM #MyTableVariable))
);
I'll stand corrected if I'm wrong (just don't have the opportunity to test it myself right now), but I believe that even if NULL was passed in as the #MyTableVariable value, SQL would still see it as an empty table (with the corresponding structure of the UDT)
SQL Server provides conditional statemets; then, you can try this approach bellow, with a conditional select, based on your table variable status (in this example, checking if is not empty):
IF(EXISTS(SELECT 1 FROM #MyTableVariable))
BEGIN
SELECT ...
FROM MyTable m
INNER JOIN #MyTableVariable v ON v.Id = m.Id
WHERE UserId = 1
END
ELSE
BEGIN
SELECT ...
FROM MyTable
WHERE UserId = 1
END

SQL filter rows based on multiple condition and get the matching records

Hello all I have a requirement where I need to filter the rows with multiple conditions and exclude the result if a single entry exists in matching. Here are my sample tables
DECLARE #CUSTOMER TABLE
(
CUSTOMERID INT,
CUSTOMERNAME NVARCHAR(100)
)
DECLARE #ORDER TABLE
(
ORDERID INT,
CUSTOMERID INT,
ISSPECIALORDER INT,
SPECIALORDERID INT
)
DECLARE #SPECIALORDERDTL TABLE
(
SPECIALORDERID INT,
SPECIALORDERDATAID INT
)
DECLARE #SPECIALORDERDATA TABLE
(
SPECIALORDERDATAID INT,
SPECIALORDERMASTERID INT
)
INSERT INTO #CUSTOMER VALUES (100,'CUSTOMER1'),(200,'CUSTOMER2'),(300,'CUSTOMER3'),(400,'CUSTOMER4`enter code here`')
INSERT INTO #ORDER VALUES (1,100,0,1),(2,100,1,1),(3,100,1,2),(4,200,0,1),(5,200,1,1),(6,200,1,4),(7,300,1,5),(8,400,1,6)
INSERT INTO #SPECIALORDERDTL VALUES(1,1),(2,1),(3,2),(4,4)
INSERT INTO #SPECIALORDERDATA VALUES(1,1),(2,1),(3,1),(4,2),(5,2) -- 2 a special order
SELECT C.CUSTOMERID,C.CUSTOMERNAME
FROM #ORDER O
INNER JOIN #CUSTOMER C ON C.CUSTOMERID=O.CUSTOMERID
INNER JOIN #SPECIALORDERDTL SO ON SO.SPECIALORDERID = O.SPECIALORDERID
INNER JOIN #SPECIALORDERDATA SOD ON SO.SPECIALORDERDATAID = SOD.SPECIALORDERDATAID
WHERE SOD.SPECIALORDERID <> 2 AND O.ISSPECIALORDER =0
GROUP BY C.CUSTOMERID,C.CUSTOMERNAME
ORDER BY C.CUSTOMERNAME
When I have an entry in #SPECIALORDERDTL with SPECIALORDERMASTERID as 2 I need to consider them as special entries and exclude those. So my query should return only the customer with 100.
It is not clear from your description or SQL what exactly want. From my understanding:
DECLARE #CUSTOMER TABLE
(
CUSTOMERID INT,
CUSTOMERNAME NVARCHAR(100)
)
DECLARE #ORDER TABLE
(
ORDERID INT,
CUSTOMERID INT,
ISSPECIALORDER INT,
SPECIALORDERID INT
)
DECLARE #SPECIALORDERDTL TABLE
(
SPECIALORDERID INT,
SPECIALORDERDATAID INT
)
DECLARE #SPECIALORDERDATA TABLE
(
SPECIALORDERDATAID INT,
SPECIALORDERMASTERID INT
)
INSERT INTO #CUSTOMER VALUES
(100,'CUSTOMER1'),
(200,'CUSTOMER2'),
(300,'CUSTOMER3'),
(400,'CUSTOMER4')
INSERT INTO #ORDER VALUES
(1,100,0,1),
(2,100,1,1),
(3,100,1,2),
(4,200,0,1),
(5,200,1,1),
(6,200,1,4),
(7,300,1,5),
(8,400,1,6)
INSERT INTO #SPECIALORDERDTL VALUES(1,1),(2,1),(3,2),(4,4)
INSERT INTO #SPECIALORDERDATA VALUES(1,1),(2,1),(3,1),(4,2),(5,2) -- 2 a special order
SELECT C.CUSTOMERID,C.CUSTOMERNAME
from #Customer c
where exists (select * from #ORDER o where o.CustomerId = c.CustomerId)
and not exists (
select *
from #ORDER O
LEFT JOIN #SPECIALORDERDTL SO ON SO.SPECIALORDERID = O.SPECIALORDERID
LEFT JOIN #SPECIALORDERDATA SOD ON SO.SPECIALORDERDATAID = SOD.SPECIALORDERDATAID
WHERE (SO.SPECIALORDERID IS NULL
or SOD.SPECIALORDERMASTERID = 2 --AND O.ISSPECIALORDER =0
) AND O.CustomerId = c.CustomerId
);
GO
CUSTOMERID | CUSTOMERNAME
---------: | :-----------
100 | CUSTOMER1
db<>fiddle here
Assuming I understand the question, I think a conditional aggregation in the having clause is probably the simplest way to get the result you want:
SELECT C.CUSTOMERID, C.CUSTOMERNAME
FROM #CUSTOMER As C
JOIN #ORDER O
ON C.CUSTOMERID = O.CUSTOMERID
JOIN #SPECIALORDERDTL SO
ON O.SPECIALORDERID = SO.SPECIALORDERID
JOIN #SPECIALORDERDATA SOD
ON SO.SPECIALORDERDATAID = SOD.SPECIALORDERDATAID
GROUP BY C.CUSTOMERID, C.CUSTOMERNAME
HAVING COUNT(CASE WHEN SOD.SPECIALORDERMASTERID = 2 THEN 1 END) = 0
The having clause will filter out every customer where at least one of the orders associated with them have a specialordermasterid of 2.
From your description it sounds like not every customer will have an entry in SPECIALORDERDTL or SPECIALORDERDTA so you don't want to inner join to those tables.
What you need is a "not exists" correlated subquery to check that the customers do not have a matching row in those tables.
So you can remove the inner joins to SPECIAL* tables and add something like:-
where not exists (select null from SPECIALORDERDTL SO where
SO.SPECIALORDERID = O.SPECIALORDERID and SO.SPECIALORDERMASTERID = 2)
From your description I'm not quite sure where "SOD.SPECIALORDERID <> 2 AND O.ISSPECIALORDER =0" fit into it, so please give further details of outputs if you can't resolve using subquery.
Following your clarification, please try something like this:-
SELECT distinct C.CUSTOMERID,C.CUSTOMERNAME
FROM #ORDER O
INNER JOIN #CUSTOMER C ON C.CUSTOMERID=O.CUSTOMERID
where not exists
(select null from #SPECIALORDERDTL SO
INNER JOIN #SPECIALORDERDATA SOD ON SO.SPECIALORDERDATAID = SOD.SPECIALORDERDATAID
where SO.SPECIALORDERID = O.SPECIALORDERID and
SOD.SPECIALORDERMASTERID = 2
)
order by C.CUSTOMERNAME

SQL Server : How to use a column value in same select statement

I have a query which is fetching values from multiple tables, it looks some thing close to the below query.
I have highlighted in bold the place where i need to pass the value returned from the same query.
select E.[EmployeeName] as EmployeeName
, (select City from tblEmployeeCities where C.EmployeeID = E.EmployeeID) as EmployeeCity
, (Select State from tblStates **where City = /i need to give the name of
the city returned by the above statement** ) as EmployeeState
from tblEmployees E
Use JOIN/LEFT JOIN:
SELECT E.EmployeeName, C.City, S.State
FROM tblEmployees E
LEFT JOIN tblEmployeeCities C ON C.EmployeeID=E.EmployeeID
LEFT JOIN tblStates S ON S.City=C.City
Maybe something like
select E.[EmployeeName] as EmployeeName
, c.City as EmployeeCity
, (Select State from tblStates where City = c.City ) as EmployeeState
from tblEmployees E
inner join tblEmployeeCities C on C.EmployeeID = E.EmployeeID
I think this can be achieved with a CASE WHEN ELSE END statment in the select part of your sql statment.
Look at example here for syntax or usage: https://www.w3schools.com/sql/func_mysql_case.asp

T-SQL join table in where exists

I have the below query
select *
from monthly_accounts m
join blocks_Dep on (m.code = d.code )
where exists (select R_code
from balance c
inner join blocks_div d on (m.b_id = d.b_id and c.name = d.name)
)
I am getting an error about an invalid m.b_i identifier; how to make join between the first table and tables in exists with the join?
Maybe you are getting that error because the operator is exists not exist. Does this work?
select m.*, d.*
from monthly_accounts m join
blocks_Dep d
on m.code = d.code
where exists (select 1
from balance b inner join
blocks_div bd
on b.name = bd.name
where m.b_id = bd.b_id
);
Note that I made other changes and fixed other problems:
blocks_dep did not have a table alias.
I assigned table aliases that are abbreviations of the table names for all tables.
I moved the correlation clause to a where rather than the on. I don't expect to see correlation clauses in an on (although the logic is correct).
You have some answers
The core should stand on it's own
Use alias and don't repeat the alias
select * from
monthly_accounts m
join blocks_Dep on (m.code=d.code)
d is not yet defined. Even the query parser figured it out I doubt that is what you wanted.
This links the not exists by m.b_id which is what I think you mean to do?
select *
from monthly_accounts m
join blocks_Dep d1
on m.code = d1.code
where not exists ( select 1
from balance c
join blocks_div d2
on d2.b_id = m.b_id
and d2.name = c.name
)

Resources