sqlserver self join - optimal way? - sql-server

I have written the below query. I do not think it is the optimum way.
The scenario is - given a child, I want to fetch all rows corresponding to its parent(root).
select * from TableA where column_root =
(select column_root from TableA where column_child = 123)

You can use JOIN:
select T1.* from
TableA T1 join
TableA T2 ON T1.column_root=T2.column_root
where T2.column_child = 123
Sample result in SQL Fiddle

Related

How can I rewrite postgres code to SQL Server?

I have a piece of code on plpgsql like that:
UPDATE table1
SET column1 = value1
FROM table1 tbl
INNER JOIN table2 tbl2 ON tbl2.id = tbl.id
WHERE table2.code = 200
AND table1.id = tbl.id;
Here table1 and alias tbl are two different table due to postgress specification.
Now I want to write the same procedure for SQL Server, but this code fails with error
[S0001][4104] The multi-part identifier "table1.id" could not be bound.
How should I rewrite this code for SQL Server?
This is simpler in SQL Server, because the UPDATE can reference an alias in the FROM. There is no need to JOIN back to the original table.
So:
UPDATE tbl
SET column1 = value1
FROM table1 tbl JOIN
table2 tbl2
ON tbl2.id = tbl.id
WHERE tbl2.code = 200;
This assumes that id is the primary key of table1 (a reasonable assumption).
You could use common table expression:
WITH cte AS (
SELECT tbl.column1
FROM table1 tbl
JOIN table2 tbl2 ON tbl2.id = tbl.id
WHERE tbl2.code = 200
)
UPDATE cte SET column1 = value1;
-- SELECT * FROM cte
By highlighting and running only select statement inside cte you could check the result before doing actual update.
db<>fiddle demo

How do I name the results of a joined table query

I have two tables and did an inner join on them based on id. Now I need to name this joined table. How do I do that? The reason I want to name this table is because I have to join this result table with some other tables.
You created what is commonly referred t as a relvar, not another table. You can join to it by placing it's SELECT definition in a sub query like this:
SELECT
FROM (
-- original join SQL
) t
INNER JOIN table2 on ...
You can create an alias for the result of a join using a subquery. For example:
select *
from (
select *
from tbl1
join tbl2
on tbl1.id = tbl2.id
) as JoinAlias
join tbl3
on JoinAlias.id = tbl3.id
You can often make do without such an alias. For example, consider a third join:
select *
from tbl1
join tbl2
on tbl1.id = tbl2.id
join tbl3
on tbl3.col1 = tbl1.col1
and tbl3.col2 = tbl2.col2
As you can see, the third join's condition can refer to columns from all earlier tables.
select * from (
select * from TableA a join TableB b on a.id = b.a_id
) as p;
As per the comments, it seems that a view might be what you are looking for:
create view MyView
as
select * from TableA a join TableB b on a.id = b.a_id;
It won't be listed in the tables (rather in the views), but will behave just like a table in your sql editor.

Join two tables only if the first contains certain values if not join another

I am trying to write a query that join to a TableA another TableB if TableA.Column1 contains numeric values and Join to TableA another TableC if TableA.Column1 contains varchar values.
Is there a way to write such a query?
How about something like this? You will need to cast the columns appropriate to some middle ground.
SELECT *
FROM TableA a
INNER JOIN TableB b ON b.Columns1 = a.Column1
AND ISNUMERIC(a.Column1) = 1
WHERE 1=1
UNION
SELECT *
FROM TableA a
INNER JOIN TableC c ON c.Columns1 = a.Column1
AND ISNUMERIC(a.Column1) = 0
The table design sounds questionable, but I think this query is a simple way to achieve what you're asking for.
SELECT
TableA.Column1,
TableB.Column2,
TableC.Column2,
ISNULL(TableB.Column2, TableC.Column2)
FROM TableA
LEFT OUTER JOIN TableB ON
ISNUMERIC(TableA.Column1) = 1
AND TableA.Column1 = TableB.Column1
LEFT OUTER JOIN TableC ON
ISNUMERIC(TableA.Column1) = 0
AND TableA.Column1 = TableC.column1
As Mike Cheel points out, you may need to do some casting.
Also, with this approach you will need to consider the possibility that there is a record in TableA that does not match anything in TableB or TableC, because this is using outer joins. If you don't want those records in your result, you can just exclude them with a condition in your WHERE clause.
Along the lines of JNK's comment, here's a way where you could go about it which at least tries to encapsulate the design issue a bit, by add 2 Computed columns to your table, which represent placeholders for the INT and VARCHAR foreign keys.
ALTER TABLE MyTable ADD IntJoinColumn AS
CASE WHEN ISNUMERIC(BadJoinColumn) = 1
THEN CAST(BadJoinColumn AS INT)
ELSE NULL
END;
ALTER TABLE MyTable ADD VarCharJoinColumn AS
CASE WHEN ISNUMERIC(BadJoinColumn) = 1
THEN NULL
ELSE BadJoinColumn
END;
You can then join in a more 'readable' manner, like so:
SELECT mt.*
FROM MyTable mt
INNER JOIN MyIntJoinTable ON IntJoinColumn = MyIntJoinTable.Id
UNION ALL
SELECT mt.*
FROM MyTable mt
INNER JOIN MyVarCharJoinTable ON VarCharJoinColumn = MyVarCharJoinTable.VarCharId;
SQLFiddle Here
(The NULL mapping has the effect of filtering out the 'incorrect' data types by eliminating them during the INNER JOIN.)

SQL Update after Joining Two Tables

I am new to SQL, using Microsoft SQL Server Management Studio.
I am trying to write a SQL statement that performs an update after two tables are joined.
I have two tables: myTable1 and myTable2. Both share a field MyID, which is going to be the field that I join on. myTable1 contains a column called BitToUpdate. And MyTable2 contains a column called BitToCheck.
I want to set BitToUpdate in myTable1 to be 1 where BitToCheck in myTable2 is 1 as well.
Here is what I have:
SELECT M.MyID, BitToUpdate, BitToCheck
INTO #temp_table
FROM myTable1 as T1
LEFT JOIN myTable2 as T2
ON M.MyId = PO.MyId
So first I tried to join the two tables myTable1 and myTable2 on their IDs, and store the result in a temporary table.
Next, I want to update BitToUpdate to be 1 where BitToCheck is 1.
So to do that in the temporary table, I have:
UPDATE #temp_table
SET
`BitToUpdate` = 1
WHERE
`BitToCheck` = 1
This updates the BitToUpdate successfully in #temp_table. However, when I do a select on myTable1, I find that BitToUpdate is not changed. I suppose this makes sense as #temp_table isn't really a "pointer"....
But what would be the correct way to approach this join and update?
You don't need to use a LEFT JOIN here, since you are checking on a condition from table 2, so an INNER JOIN should be better here.
UPDATE T1
SET T1.BitToUpdate = 1
FROM myTable1 T1
INNER JOIN myTable2 T2
ON T1.MyId = T2.MyId
WHERE T2.BitToCheck = 1
What you are doing in your first query is updating a temp table named #temp. the updates never go to the actual table myTable1 or mayTable2. To update records while joining with other tables try this:
UPDATE T1
SET T1.BitToUpdate = 1
FROM myTable1 as T1
LEFT JOIN myTable2 as T2 (ON T1.MyId = T2.MyId)
WHERE T2.BitToCheck = 1
--SELECT M.MyID, BitToUpdate, BitToCheck
--INTO #temp_table
update t1
set t1.BitToUpdate = 1
FROM myTable1 as T1
LEFT JOIN myTable2 as T2
ON t1.MyId = t2.MyId
where t2.bittocheck = 1
UPDATE T1
SET BitToUpdate=1
FROM myTable1 T1
LEFT JOIN myTable2 T2
ON T1.MyId=T2.MyId
WHERE T2.BitToCheck=1

Optimize CASE Test in SQL Server

I'm wondering if there's any way to optimize the following SELECT query. (Note: I typed this when writing my question for nonexistent tables and I might not have the correct syntax.)
The goal is, if Table2 contains any related rows I want to set the value of the third column to the number of related rows in Table2. Otherwise, if Table3 contains any related rows I want to set the column to the number of related rows in Table3. Otherwise, I want to set the column value to 0.
SELECT Id, Title,
CASE
WHEN EXISTS (SELECT * FROM Table2 t2 WHERE t2.RelatedId = Table1.Id) THEN
(SELECT COUNT(1) FROM Table2 t2 WHERE t2.RelatedId = Table1.Id)
WHEN EXISTS (SELECT * FROM Table3 t3 WHERE t3.RelatedId = Table1.Id) THEN
(SELECT COUNT(1) FROM Table3 t3 WHERE t3.RelatedId = Table1.Id)
ELSE 0
END AS RelatedCount
FROM Table1
I don't like the fact that I'm basically performing the same query twice (in two cases). Is there any way to do what I want while only performing the query once?
Note that this is part of a much larger query with multiple JOINs and UNIONs so it's not easy to take a completely different approach.
This query should perform much better. You are not just performing the same query twice; since they are correlated subqueries, they will run once per row.
SELECT Id, Title,
coalesce(t2.Count, t3.Count, 0) AS RelatedCount
FROM Table1 t
left outer join (
SELECT RelatedId, count(*) as Count
FROM Table2
group by RelatedId
) t2 on t1.Id = t2.RelatedId
left outer join (
SELECT RelatedId, count(*) as Count
FROM Table3
group by RelatedId
) t3 on t1.Id = t3.RelatedId

Resources