Is it possible to do the following:
IF [a] = 1234 THEN JOIN ON TableA
ELSE JOIN ON TableB
If so, what is the correct syntax?
I think what you are asking for will work by joining the Initial table to both Option_A and Option_B using LEFT JOIN, which will produce something like this:
Initial LEFT JOIN Option_A LEFT JOIN NULL
OR
Initial LEFT JOIN NULL LEFT JOIN Option_B
Example code:
SELECT i.*, COALESCE(a.id, b.id) as Option_Id, COALESCE(a.name, b.name) as Option_Name
FROM Initial_Table i
LEFT JOIN Option_A_Table a ON a.initial_id = i.id AND i.special_value = 1234
LEFT JOIN Option_B_Table b ON b.initial_id = i.id AND i.special_value <> 1234
Once you have done this, you 'ignore' the set of NULLS. The additional trick here is in the SELECT line, where you need to decide what to do with the NULL fields. If the Option_A and Option_B tables are similar, then you can use the COALESCE function to return the first NON NULL value (as per the example).
The other option is that you will simply have to list the Option_A fields and the Option_B fields, and let whatever is using the ResultSet to handle determining which fields to use.
This is just to add the point that query can be constructed dynamically based on conditions.
An example is given below.
DECLARE #a INT = 1235
DECLARE #sql VARCHAR(MAX) = 'SELECT * FROM [sourceTable] S JOIN ' + IIF(#a = 1234,'[TableA] A ON A.col = S.col','[TableB] B ON B.col = S.col')
EXEC(#sql)
--Query will be
/*
SELECT * FROM [sourceTable] S JOIN [TableB] B ON B.col = S.col
*/
You can solve this with union
select a, b
from tablea
join tableb on tablea.a = tableb.a
where b = 1234
union
select a, b
from tablea
join tablec on tablec.a = tableb.a
where b <> 1234
I disagree with the solution suggesting 2 left joins. I think a table-valued function is more appropriate so you don't have all the coalescing and additional joins for each condition you would have.
CREATE FUNCTION f_GetData (
#Logic VARCHAR(50)
) RETURNS #Results TABLE (
Content VARCHAR(100)
) AS
BEGIN
IF #Logic = '1234'
INSERT #Results
SELECT Content
FROM Table_1
ELSE
INSERT #Results
SELECT Content
FROM Table_2
RETURN
END
GO
SELECT *
FROM InputTable
CROSS APPLY f_GetData(InputTable.Logic) T
I think it will be better to think about your query in a different way and treat them more like sets.
I do believe if you make two separate queries then join them using UNION, It will be much better in performance and more readable.
Related
Here is my query
select order_no, pkg_no, zone_no
from T_DETAIL_ITEM a
where order_no = 495
order by order_no, pkg_no
For a given package I have zone number = 0
What I need to do is return all the lines with the pkg_no = 1597. Because one where exists with a zero zone.
I tried a few different 'where exists' lines and it isn't working.
Try to self join.
This way you can put your requirement in the second table "instance" but retrieve everything from that table matches based on another common field.
select distinct a.order_no, a.pkg_no, a.zone_no
from T_DETAIL_ITEM a
join T_DETAIL_ITEM b on b.pkg_no = a.pkg_no
where b.zone_no = 0
order by a.order_no, a.pkg_no
The accepted answer is good, but I had saw that you noted you tried EXISTS, so I wrote this example up using that method.
SELECT *
FROM T_Detail_Item d
WHERE exists (SELECT * FROM T_Detail_Item dx WHERE dx.pkg_no = d.pkg_no AND dx.zone_no = 0)
One way is to self reference the table in a left join and only include those with a zone_no=0, within the join clause. The filter out non-matching records by excluding records that do not match from the left join, T2.pkg_no = NULL.
SELECT
T1.pkg_no, T1.zone_no
FROM
T_DETAIL_ITEM T1
LEFT OUTER JOIN T_DETAIL_ITEM T2 ON T2.pkg_no = T1.pkg_no AND T2.zone_no = 0
WHERE
NOT T2.pkg_no IS NULL
If I correctly understood the question, you need something like this
BEGIN
declare #str varchar(max);
set #str='';
SELECT #str=#str+ [YOURCOLUMB]
FROM [YOURTABLE]
SELECT #str
END
If B.[Name] has value it well select the the following
Select
B.[Name],
A.[Address],
A.TIN,
A.AuthorizedRep1Name,
A.AuthorizedRep1Email,
A.AuthorizedRep1Contact,
A.AuthorizedRep2Name,
A.AuthorizedRep2Email,
A.AuthorizedRep2Contact,
A.RefundOfSecDep,
A.PayeeAddress,
A.PayeeTIN
From MembersTbl A
join UserTbl B
on A.UserID = B.ID
And if the B.[Name] is Null It will Select the following
Select
A.[Address],
A.TIN,
A.AuthorizedRep1Name,
A.AuthorizedRep1Email,
A.AuthorizedRep1Contact,
A.RefundOfSecDep,
A.PayeeAddress,
A.PayeeTIN
From MembersTbl A
What will I do?
You can check with creating a variable and assigning their values as shown below.
declare #name varchar(20)
set #name = (select name from YourTable b where ...)
Now to check
if(#name is not null)
begin
----Your first query.
end
else
begin
----Your second query
end
You don't need to do extra effort just add left join in your second query as both query return same data except extra columns.
You can use result according to you requirement based on your condition like B.Name is null or not.
Example :
Select
B.[Name],
A.[Address],
A.TIN,
A.AuthorizedRep1Name,
A.AuthorizedRep1Email,
A.AuthorizedRep1Contact,
A.AuthorizedRep2Name,
A.AuthorizedRep2Email,
A.AuthorizedRep2Contact,
A.RefundOfSecDep,
A.PayeeAddress,
A.PayeeTIN
From MembersTbl A
left join UserTbl B
on A.UserID = B.ID
I hope it will help you :)
I have following query, which is working as expected but taking approx 3 seconds to execute. Reason is large number of records. Can somebody please suggest any steps in order to improve performance?
Explanation :
Check to see value using Comp id and Default_Comp = 1
If not found, ignore the Default_Comp and check only based on Comp id
Still not found, ignore the join with table 2 and try to get by Comp id.
My code:
DECLARE #Finished_Comp VARCHAR(MAX) = NULL;
SELECT #Finished_Comp = MIN(tbl2.Finished_Comp)
FROM Table1 tbl1
INNER JOIN Table2 tbl2 ON tbl1.Sav_ID = tbl2.Sav_ID
WHERE Comp_ID = #Comp_ID AND tbl1.Default_Comp = 1
IF #Finished_Comp IS NULL
BEGIN
SELECT #Finished_Comp = MIN(tbl2.Finished_Comp)
FROM Table1 tbl1
INNER JOIN Table2 tbl2 ON tbl1.Sav_ID = tbl2.Sav_ID
WHERE Comp_ID = #Comp_ID
END
IF #Finished_Comp IS NULL
BEGIN
SELECT #Finished_Comp = MIN(Finished_Comp)
FROM Table1 tbl1
WHERE Comp_ID = #Comp_ID AND #Finished_Comp != ''
END
I tried to use COALESCE, but it's returning wrong results for Finished_Comp
You say in the comments
I strongly believe the query can be changed to some extent so that no
multiple queries need to be executed.
Yes you're right.
SELECT #Finished_Comp = COALESCE(MIN(CASE WHEN tbl1.Default_Comp = 1 THEN tbl2.Finished_Comp END),
MIN(tbl2.Finished_Comp),
MIN(CASE WHEN tbl1.Finished_Comp <> '' THEN tbl1.Finished_Comp END))
FROM Table1 tbl1
LEFT JOIN Table2 tbl2
ON tbl1.Sav_ID = tbl2.Sav_ID
WHERE tbl1.Comp_ID = #Comp_IDV
But at best this will only reduce execution time to a third of current (for the case that all three queries need to be executed).
You should consider adding indexes on
Table1 - Comp_ID, Sav_ID INCLUDE (Default_Comp, Finished_Comp)
Table2 - Sav_ID INCLUDE (Finished_Comp)
For potentially much larger improvements.
The three tables in question are:
Table A - relevant columns are TimeTicket and IdAddress
Table B - relevant columns are CommunicationNumber, TimeCreate and IdAddress.
Table C - relevant columns are CommunicationNumber, LastCalled, NextCall
Table C is created by a join of TableA and TableB on IdAddress
INSERT INTO tblC ([CommunicationNumber], [LastCalled] ,[NextCall])
SELECT T2.CommunicationNumber, T2.TimeCreate, T1.TimeTicket
FROM tblA T1
INNER JOIN tblB T2
ON T1.IdAddress = T2.IdAddress AND T2.CommunicationNumber IS NOT NULL
That's one part of the process, and that's fine.
Now, when there is new data in Table A and Table B, I want to update the data entries in Table C. However, I want to ignore the values from Table A and Table B that I have already entered into Table C.
To achieve this, I used NOT EXISTS and wrote a query that looks like this.
INSERT INTO tblC ([CommunicationNumber], [LastCalled] ,[NextCall])
SELECT T2.CommunicationNumber, T2.TimeCreate, T1.TimeTicket
FROM tblA T1
INNER JOIN tblB T2
ON T1.IdAddress = T2.IdAddress AND T2.CommunicationNumber IS NOT NULL
WHERE NOT EXISTS (SELECT T3.CommunicationNumber
FROM [dbo].[tblPhoneLogRep] T3
WHERE T1.TimeTicket <> T3.NextCall AND T2.TimeCreate <> T3.LastCalled AND T2.CommunicationNumber <> T3.CommunicationNumber)
However, this query always returns an empty set.
Could someone please explain to me what is it that I am doing incorrectly?
Try using the EXCEPT set operator:
INSERT INTO tblC ([CommunicationNumber], [LastCalled] ,[NextCall])
SELECT T2.CommunicationNumber, T2.TimeCreate, T1.TimeTicket
FROM tblA T1
INNER JOIN tblB T2
ON T1.IdAddress = T2.IdAddress AND T2.CommunicationNumber IS NOT NULL
EXCEPT
SELECT CommunicationNumber, LastCalled, NextCall FROM tblC
To fix your existing query, you would need to change your <> operators to = operators, like so:
INSERT INTO tblC ([CommunicationNumber], [LastCalled] ,[NextCall])
SELECT T2.CommunicationNumber, T2.TimeCreate, T1.TimeTicket
FROM tblA T1
INNER JOIN tblB T2
ON T1.IdAddress = T2.IdAddress AND T2.CommunicationNumber IS NOT NULL
WHERE NOT EXISTS (SELECT 1
FROM tblC
WHERE T1.TimeTicket = tblC.NextCall AND T2.TimeCreate = tblC.LastCalled AND T2.CommunicationNumber = tblC.CommunicationNumber)
Personally, I think the EXCEPT syntax is more clear though.
Your issue is that you are essentially using a double negative. You are saying NOT EXISTS and you are setting your WHERE criteria to <>. I think it would work out if you either used EXISTS or change you criteria =.
None of my SQL Server stored procedure editing IDEs seem to have any tools to enforce indentation styles, so I find that a lot of the stored procedures I see are all over the place. I find indenting really improves readability though. I would like to codify some stored procedure indenting standards in our company's coding style guide, and I'm wondering if anyone has any best practices they would like to share.
For instance, in a normal SELECT statement, I try to keep the SELECT, FROM, WHERE, ORDER BY, and GROUP BY clauses all on the same level, and indent anything below that. I also try to indent each JOIN one level from the table it's logically joining into.
Does anyone else have similar advice or best practices?
My select formattings:
--
-- SELECT statements
--
select
t1.field1, t1.field2,
t2.field3,
t3.fieldn
from
tblOne t1
inner join tblTwo t2 on t1.field = t2.field and t1.field2 = t2.field2
left join tblThree t3 on t2.field = t3.field and t2.field2 = t3.field2
left join (
select id, sum(quantity) as quantity
from tbl4
group by id
) t4 on t4.id=t3.id
where
t1.field = 'something'
and t2.field = 'somethin else'
order by
fieldn
Optionally (when lines get too long) I split lines at logical boundaries and indent splitted parts:
inner join tblTwo as t2
on t1.field = t2.field and t1.field2 = t2.field2
Sometimes I'm using different syntax for very simple (sub)selects.
Main goal is to make code readable and relatively easily modifyable.
--edit--
IMHO (at least in small team) it is not needed to enforce very strict rules, this helps support and maintainig :) In our team, where about 3-4 people write most sql, it is very easy to establish code author, just looking at sql statement - all people are using somewhat different style (capitalzing, aliases, indenting etc).
SELECT T1.Field1,
T1.Field2,
T2.Field1 As Field 3
FROM Table1 AS T1
LEFT JOIN Table2 AS T2
ON T1.Field1 = T2.Field7
WHERE T1.Field9 = 5
AND T2.Field1 < 900
ORDER BY T2.Field1 DESC
INSERT INTO Table1 (
Field1,
Filed2,
Field3 )
VALUES ( 'Field1',
'Field2',
'Field3' )
UPDATE Table1
SET Field1 = SomeValue,
Field2 = AnotherValue,
FIeld134567 = A ThirdValue
WHERE Field9 = A Final Value
I find that I dont necessarily has a set indentation length and instead I try to indent based on the length of the field names and values. I like my left margins to line up along any given vertical plane and I like my Evaluators (such as equal signs) to line up. I always have any command term on a different vertical plane than its accompanying values and fields. I also tend to try to make the space between my SELECT command and the Field list equal in length to the space used by a SELECT DISTINCT Field or INSERT INTO Table.
But in the end, all that is just my preferences. I like neat looking code.
I prefer the following style:
--
-- SELECT statements
--
select field1,
field2,
field3,
fieldn
from tblOne as t1
inner join tblTwo as t2
on t1.field = t2.field
and t1.field2 = t2.field2
left outer join tblThree as t3
on t2.field = t3.field
and t2.field2 = t3.field2
where t1.field = 'something'
and t2.field = 'somethin else'
order by fieldn
--
-- IF statements
--
if #someVar = 'something'
begin
-- statements here
set #someVar2 = 'something else'
end
--
-- WHILE statements
--
while #count < #max
begin
set #count = #count + 1
end
I prefair the following style...
Select
Id = i.Identity,
User = u.UserName,
From
tblIdentities i
Inner Join
tblUsers u On i.UserId = u.UserId
Where
(
u.IsActive = 'True'
And
i.Identity > 100
)
Also I try and not to use the As keyword. I prefair equals instead. Probably upset a few people but I find this code much easier to read...
Select
Id = tbl.Identity,
User = tbl.UserName,
Age = tbl.Age,
DOB = tbl.DateOfBirth
From
tbl
Rather than...
Select
tbl.Id As Identity,
tbl.UserName As User,
tbl.Age As Age,
tbl.DateOfBirth As DOB
From
tbl
SELECT T1.Field1,
T1.Field2,
T2.Field1 AS Field 3
FROM Table1 AS T1
LEFT JOIN Table2 AS T2 ON T1.Field1 = T2.Field7
WHERE T1.Field9 = 5
AND T2.Field1 < 900
ORDER BY T2.Field1 DESC
INSERT INTO Table1 (Field1, Field2, Field3)
VALUES ('Field1', 'Field2', 'Field3' ) /* for values trivial in length */
UPDATE Table1
SET Field1 = SomeValue,
Field2 = AnotherValue,
FIeld134567 = A ThirdValue
WHERE Field9 = A Final Value
I think my preferred format comes from that one COBOL class I took back in college. Something about code in pretty aligned columns that makes me happy inside.
My style is almost identical to Justin's. I indent the "and" so the "d" in "and" lines up with the "e" in "where".
Sometimes I capitalize keywords. When I have a sub-select, I indent the whole sub-select and format it the same as a regular select.
One place where I may deviate is if I have dozens of fields being selected. In that case, I put several fields to a line and add white space to make even columns of text.
I tend to right-justify the keywords:
SELECT T1.Field1, T2.Field2
FROM Table1 AS T1
LEFT JOIN Table2 AS T2 ON T1.Field1 = T2.Field7
WHERE T1.Field9 = 5
AND T2.Field1 < 900
ORDER BY T2.Field1 DESC
Note that it's not hard-and-fast. I favor having the SELECT being left-most that I will break the justification (INNER JOIN, ORDER BY). I'll wrap on ON and its ilk if necessary, preferring to start a line with a keyword, if possible.
LEFT JOIN Table2 AS T2
ON T1.Field1 = T2.Field7 AND T2.Field8 IS NOT NULL