How to insert artificial data into a select statement - sql-server

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 ...

Related

Combining columns of two different datasets

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);

Selecting a set of rows more than once

Is there a simple, concise way to select the same set of rows repeated based on a count held in a variable, without using a loop?
For instance, suppose SELECT a, b, c FROM foo WHERE whatsit = something returns
a b c
--- --- ----
1 2 3
...and I have #count with 3 in it. Is there a reasonable way without a loop to get:
a b c
--- --- ----
1 2 3
1 2 3
1 2 3
? Order doesn't matter, and I don't need to know which group any given row belongs to. I actually only need this for one row (as above), and a solution that only works for one row would do the trick, but I assume if we can do it for one, we can do it for any number.
Try with a Recursive CTE
WITH cte
AS (SELECT 1 AS id,a,b,c
FROM tablename
UNION ALL
SELECT id + 1,a,b,c
FROM cte
WHERE id < 3) --#count
SELECT a,b,c
FROM cte
Another way to do using cross join
SELECT a, b, c
FROM Table1
CROSS JOIN (SELECT number
FROM master.dbo.spt_values
WHERE type = 'P'
AND number BETWEEN 1 AND 3) T
I don't know of a way you could do this without a loop or dynamic SQL. I think a union is all that I can come up with
select q1.a,q1.b,q1.c
from (
SELECT a, b, c FROM foo
union all
SELECT a, b, c FROM foo
union all
SELECT a, b, c FROM foo ) q1
order by q1.a

SQL join multiple tables - result set not expected

I am working in SQL Server 2008. I have 4 tables I want join. Let us call them tables A, B, C, and D. B, C, and D are all subsets of table A. There could be some records that are common amongst B, C, and D. My goal is to select all records in A that are not in B, C, or D. So, I think the correct query to run is:
SELECT
A.x
FROM A
LEFT JOIN B
ON A.x = B.y
LEFT JOIN C
ON A.x = C.z
LEFT JOIN D
ON A.x = D.i
WHERE
(
(B.y IS NULL)
AND
(C.z IS NULL)
AND
(D.i IS NULL)
)
The problem I am having is that I know that there are some records in table B that are returning in this result set which should not be. (The same could hold for tables C and D as well.) So, something must be wrong with my query. My best guess is that the joins are vague. The first one should give me all records in A that are not in B. Similarly, the second one should give me all records in A that are not in C. Because I have used AND in the WHERE clause, then I should essentially be returning only the records that are common to each of the joins. But, something is going wrong. How do I correct this?
Try this:
SELECT x FROM A
EXCEPT
SELECT x FROM
(
SELECT y FROM B UNION
SELECT z FROM C UNION
SELECT i FROM D
) T(x)

Counts rows returned by query that uses distinct

I have a simple query in this form:
SELECT DISTINCT a, b, c
FROM MyTable
WHERE a = SomeConditionsEtc
etc...
But I need to know how many rows it's going to return. Initially I was doing this:
SELECT COUNT(DISTINCT a)
FROM MyTable
WHERE a = SomeConditionsEtc
But that's not reliable in case a contains duplicates where the other don't. So now I'm using a nested query:
SELECT COUNT(*)
FROM (SELECT DISTINCT a, b, c
FROM MyTable
WHERE a = SomeConditionsEtc) AS Temp
Is that the correct approach or is there a better way?
Your query is straight to the point, does the job, and it's simple enough, I'm sure you can bake some unnecessary rocket science into it, but would be overblown imho. Aside from what you have, you can use a group by like below to illustrate what I mean, but you will be basically doing the same thing, getting the uniques and counting them.
SELECT COUNT(1)
FROM (SELECT a
FROM MyTable
WHERE a = 'a'
GROUP BY a, b, c) Temp

How to reuse calculated columns avoiding duplicating the sql statement

I have a lots of calculated columns and they keep repeating themselves, one inside of the others, including nested cases statements.
There is a really simplified version of something that I've searching a way to do.
SELECT
(1+2) AS A,
A + 3 AS B,
B * 7 AS C
FROM MYTABLE
You could try something like this.
SELECT
A.Val AS A,
B.Val AS B,
C.Val AS C
FROM MYTABLE
cross apply(select 1 + 2) as A(Val)
cross apply(select A.Val + 3) as B(Val)
cross apply(select B.Val * 7) as C(Val)
You can't reference just-created expressions by later referencing their column aliases. Think of the entire select list as being materialized at the same time or in random order - A doesn't exist yet when you're trying to make an expression to create B. You need to repeat the expressions - I don't think you'll be able to make "simpler" computed columns without repeating them, and views the same - you'll have to nest things, like:
SELECT A, B, C = B * 7
FROM
(
SELECT A, B = A + 3
FROM
(
SELECT A = (1 + 2)
) AS x
) AS y;
Or repeat the expression (but I guess that is what you're trying to avoid).
Another option if someone is still interested:
with aa(a) as ( select 1+2 )
, bb(b) as ( select a+3 from aa )
,cc(c) as ( select b*7 from bb)
SELECT aa.a, bb.b, cc.c
from aa,bb,cc
The only way to "save" the results of your calculations would be using them in a subquery, that way you can use A, B and C. Unfortunately it cannot be done any other way.
You can create computed columns to represent the values you want. Also, you can use a view if your calculations are dependent on data in a separate table.
Do you want calculated results out of your table? In that case you can put the relevant calculations in scalar valued user defined function and use that inside your select statement.
Or do you want the calculated results to appear as columns in the table, then use a computed column:
CREATE TABLE Test(
ID INT NOT NULL IDENTITY(1,1),
TimesTen AS ID * 10
)

Resources