Let's say I have table T1 with three int columns, A, B, and C.
I need to update column C after the update of both columns A and B.
This can be done by:
declare #one = (select col1 from T2)
update T1 set A= #one * B
update T1 set C = A+B
however
update T1 set A=#one * B, C = A+B .
Will use the values of A and B before this updated statement.
Can we join the first two statements in one statement?
Related
I have a UDF that needs to always return the same dataset structure, columns a, b, c and d.
It needs to return a UNION ALL from more than one datasource, including other UDF:s.
Let's say I have another function (myOtherUDF) that returns column a and b.
I also have a table (myTable) with the column names a, b, c and d.
What I want to do is to UNION ALL on myOtherUDF and myTable in a way that the columns c and d are added to myOtherUDF.
i.e. I want this to work although myOtherUDF lacks the columns c and d:
CREATE FUNCTION myUDF (#param INT)
RETURNS #tbl TABLE
(
a int NOT NULL,
b int NOT NULL,
c int NOT NULL,
d int NOT NULL
)
AS
BEGIN
INSERT INTO #tbl
SELECT * FROM myTable
UNION ALL // this will obviously not work
SELECT * FROM myOtherUDF(#param)
RETURN
END
I cannot use a process to preload a table and I cannot use a view since I need the parameter #param.
If you explicitly list your columns - which is best practice - you immediately solve this problem, well after adding defaults for c and d from myOtherUDF;
INSERT INTO #tbl (a, b, c, d)
SELECT a, b, c, d
FROM myTable
UNION ALL
SELECT a, b, 0, 0
FROM myOtherUDF(#param);
RETURN;
To be clear you should pretty much never select * and never insert into table without listing the columns. It saves so many issues down the line.
And for performance reasons its nearly always better to use an inline table valued function e.g.
CREATE FUNCTION myUDF
(
#param INT
)
RETURNS TABLE
RETURN
SELECT a, b, c, d
FROM myTable
UNION ALL
SELECT a, b, 0, 0
FROM myOtherUDF(#param);
I want to insert SQL Server IDENTITY values into a table A. But I want to skip few values from inserting INTO table A based on table B values. i.e If Table B has values ( 1, 34 , 100 ) THEN table A should not insert these values and go to next available value. Is that possible? If so can you please help me out on this?
Assuming that the data is currently in table_c, then:
INSERT INTO table_a (w, x, y, z, val)
SELECT c.w, c.x, c.y, c.z, c.val
FROM table_c c
LEFT JOIN table_b b ON b.val = c.val
WHERE b.val IS NULL
Does anybody knows if DB2 for i (V6r1) supports something like
INSERT ON DUPLICATE KEY UPDATE.
I need to do update with joins but don't want to use subqueries.
You can solve this by using 'MERGE'.like this:
1.Step One:table 'TEST' has a table structure (A,B,C) with a prime key 'A', existing a data record(1,2,3).
2.Step two:now you can insert a record (1,9,9) by using this SQL command as follows:
MERGE INTO TEST AS T
USING (VALUES( 1, 9, 9)) AS DAT(A, B, C)
ON T.A = DAT.A
WHEN MATCHED THEN UPDATE SET T.C = DAT.C
WHEN NOT MATCHED THEN INSERT (A, B, C) VALUES (DAT.A, DAT.B, DAT.C)
In IBM i v7.1 you will have the new MERGE statement
In v6.1, you can UPDATE where a matching row is found, and then another statement to INSERT where there is no match.
For example, an update might look like this:
UPDATE targetfile as u
SET ( flda,fldb,fldc ) =
(SELECT a, b, c
FROM inpdata as i
WHERE u.keyfld = i.keyfld
)
WHERE u.keyfld IN
(SELECT i.keyfld
FROM inpdata as i
);
then
INSERT INTO targetfile
(keyfld, flda, fldb, fldc)
SELECT keyfld, a, b, c
FROM inpdata as i
WHERE i.keyfld NOT IN
(SELECT t.keyfld
FROM targetfile as t
);
or
INSERT INTO targetfile
(keyfld, flda, fldb, fldc)
SELECT keyfld, a, b, c
FROM inpdata as i
LEFT EXCEPTION JOIN targetfile as t
ON i.keyfld = t.keyfld;
I'm trying to select records with a statement
SELECT *
FROM A
WHERE
LEFT(B, 5) IN
(SELECT * FROM
(SELECT LEFT(A.B,5), COUNT(DISTINCT A.C) c_count
FROM A
GROUP BY LEFT(B,5)
) p1
WHERE p1.c_count = 1
)
AND C IN
(SELECT * FROM
(SELECT A.C , COUNT(DISTINCT LEFT(A.B,5)) b_count
FROM A
GROUP BY C
) p2
WHERE p2.b_count = 1)
which takes a long time to run ~15 sec.
Is there a better way of writing this SQL?
If you would like to represent Set Difference (A-B) in SQL, here is solution for you.
Let's say you have two tables A and B, and you want to retrieve all records that exist only in A but not in B, where A and B have a relationship via an attribute named ID.
An efficient query for this is:
# (A-B)
SELECT DISTINCT A.* FROM (A LEFT OUTER JOIN B on A.ID=B.ID) WHERE B.ID IS NULL
-from Jayaram Timsina's blog.
You don't need to return data from the nested subqueries. I'm not sure this will make a difference withiut indexing but it's easier to read.
And EXISTS/JOIN is probably nicer IMHO then using IN
SELECT *
FROM
A
JOIN
(SELECT LEFT(B,5) AS b1
FROM A
GROUP BY LEFT(B,5)
HAVING COUNT(DISTINCT C) = 1
) t1 On LEFT(A.B, 5) = t1.b1
JOIN
(SELECT C AS C1
FROM A
GROUP BY C
HAVING COUNT(DISTINCT LEFT(B,5)) = 1
) t2 ON A.C = t2.c1
But you'll need a computed column as marc_s said at least
And 2 indexes: one on (computed, C) and another on (C, computed)
Well, not sure what you're really trying to do here - but obviously, that LEFT(B, 5) expression keeps popping up. Since you're using a function, you're giving up any chance to use an index.
What you could do in your SQL Server table is to create a computed, persisted column for that expression, and then put an index on that:
ALTER TABLE A
ADD LeftB5 AS LEFT(B, 5) PERSISTED
CREATE NONCLUSTERED INDEX IX_LeftB5 ON dbo.A(LeftB5)
Now use the new computed column LeftB5 instead of LEFT(B, 5) anywhere in your query - that should help to speed up certain lookups and GROUP BY operations.
Also - you have a GROUP BY C in there - is that column C indexed?
If you are looking for just set difference between table1 and table2,
the below query is simple that gives the rows that are in table1, but not in table2, such that both tables are instances of the same schema with column names as
columnone, columntwo, ...
with
col1 as (
select columnone from table2
),
col2 as (
select columntwo from table2
)
...
select * from table1
where (
columnone not in col1
and columntwo not in col2
...
);
Suppose we got a original query as follows:
SELECT A, B, C FROM tblA
Now, I need to additional artificial rows like
SELECT 'Kyala', B, C FROM tblA when, for example, C = 100 to be inserted into the resultset.
As an example, if the tblA hold one row:
A B C
John 1 100
my goal is to return two rows like below with a single SQL query.
A B C
John 1 100
Kyala 1 100
How could I achieve it using a single SQL instead of relying on table variable or temp table?
Just refined the query to resolve error on Union:
SELECT A, B, C from tblA
UNION
SELECT 'Kyala' as A, B, C FROM tblA WHERE C = 100
And if you don't want the others where c=100 and still getting the A in the result (from the first Select in the union), you can do it like:
SELECT A, B, C from tblA WHERE C <> 100
UNION
SELECT 'Kyala', B, C FROM tblA WHERE C = 100
or
SELECT CASE(C)
when 100 then 'Kyala'
else A
END as A, B, C from tblA
You can use a CASE:
SELECT B, C,
CASE
WHEN C = 100 THEN 'Kyala'
ELSE A
END
FROM tblA
You could achieve this with the UNION operator.
SELECT A, B, C from tblA
UNION
SELECT 'Kyala', B, C FROM tblA WHERE C = 100
In response to the question in the comments about improving performance so that the table is only queried once - you could add a covering index over columns C and B so that the second part of the query uses that index rather than querying the table:
CREATE NONCLUSTERED INDEX [IX_tblA_CD] ON [dbo].[tblA]
(
[C] ASC
)
INCLUDE ( [B]) ON [PRIMARY]
GO
However, depending on the use case (this sounds like some kind of ad-hoc process for testing?), you might prefer to take the hit of two table scans rather than adding a new index which might not be appropriate for use in production.
You can use UNIION statement:
SELECT A, B, C FROM tblA
UNION
SELECT 'Kyala', B, C FROM tblA WHERE C = 100
I need to additional artificial rows like SELECT 'Kyala', B, C FROM
tblA when, for example, C = 100 to be
inserted into the resultset.
Now, read up on....
* IF in SQL Server
*SWITCH etc.
Basically, you can define an additional column as was shown
(SELECT 'test', A, B, C FROM...)
But instead of 'test' you can put in an if or switch and work with the other fields to determine the exact stuff to output.
SELECT IF (xxxx) AS FirstColumn, A, B,
C FROM ...