When select from insert into returns no values do something different - sql

I want to insert rows into a table. The table is empty when I start. My query is as follows:
Select TOP 1 *
INTO #Result
FROM #SmallTable
WHERE CategoryID=11
ORDER BY ExpValue DESC;
It works flawless. But I want now to account for the case where the this returns no value. But I'm not sure how to approach this.
I could either make a case and select and ask if SELECT TOP 1 returns any values. Or I could check after I insert if there is a value present. But which approach would be better? Or is there an even better one?

You could use a union trick here to insert a dummy value should the first query not return any records:
INSERT INTO #Result (col)
SELECT TOP 1 col
FROM
(
SELECT TOP 1 col, 1 AS pos FROM #SmallTable WHERE CategoryID = 11 ORDER BY ExpValue DESC
UNION ALL
SELECT 'NA', 2
) t
ORDER BY pos;

Look at ##ROWCOUNT
This returns the number of rows affected by the last procedure.
IF ##ROWCOUNT = 0
PRINT 'Warning: No rows were inserted';

You can use apply :
select top (1) coalesce(st.CategoryID, 0) as CategoryID, . .
into #destination
from ( values (11)
) t(CategoryID) left join
#SmallTable st
on st.CategoryID = t.CategoryID
order by st.ExpValue desc;

Related

Given a specific column value, merge two columns in T-SQL

I have a table with the following content (simplified):
And this is the desired result:
In short, the first column has hundreds of values and sometimes repeated, for a given value of IDPRODUCTFIRST I want a RESULT column with the given value + the values ​​of IDPRODUCTSECOND.
SELECT IDPRODUCTSECOND AS RESULT
FROM [SCIOHIST].[dbo].[RELATIONPRODUCTMATCHES]
WHERE IDPRODUCTFIRST = 228697
With the query above, I can only get the values ​​from the second column, how could I add to the result column the given value (e.g. 228697) from the first column?
One method is to unpivot and select distinct values:
SELECT DISTINCT v.RESULT
FROM [SCIOHIST].[dbo].[RELATIONPRODUCTMATCHES] RPM CROSS APPLY
(VALUES (IDPRODUCTFIRST), (IDPRODUCTSECOND)) V(RESULT)
WHERE IDPRODUCTFIRST = 228697;
SELECT DISTINCT IDPRODUCTFIRST AS RESULT
FROM [SCIOHIST].[dbo].[RELATIONPRODUCTMATCHES]
--WHERE IDPRODUCTFIRST = 228697
UNION
SELECT DISTINCT IDPRODUCTSECOND AS RESULT
FROM [SCIOHIST].[dbo].[RELATIONPRODUCTMATCHES]
--WHERE IDPRODUCTFIRST = 228697
where clauses can exist or not.
IF you want duplicate value in both column are in your result you can use from "UNION ALL" instead of "UNION".
You can use Union
; With cteProd
as
(
SELECT IDPRODUCTFIRST, IDPRODUCTSECOND
FROM [SCIOHIST].[dbo].[RELATIONPRODUCTMATCHES]
)
Select RESULT from
(
SELECT IDPRODUCTFIRST, IDPRODUCTFIRST AS RESULT
FROM cteProd
Union
SELECT IDPRODUCTFIRST, IDPRODUCTSECOND AS RESULT
FROM cteProd
) Q
WHERE IDPRODUCTFIRST = 228697
Here is the fiddle
Yet another option is UNPIVOT
Example
Declare #YourTable Table ([IDPRODUCTFIRST] varchar(50),[IDPRODUCTSECOND] varchar(50)) Insert Into #YourTable Values
(228697,228699)
,(228697,228701)
Select Distinct Result
From (Select [IDPRODUCTFIRST],[IDPRODUCTSECOND]
From #YourTable
Where [IDPRODUCTFIRST] = 228697
) a
Unpivot ( Result for Item in ([IDPRODUCTFIRST],[IDPRODUCTSECOND]) ) unp
Returns
Result
228697
228699
228701

SQL query to select with Range condition in source table

Have a scenario to select the value from table where range condition is present in source table.
Like,
TableA
ID value condition
1 20 A-M
2 50 N-Z
Select value from TableA where condition = 'C%'
--want to select TableA value from TableB by passing person name starts with like,
-- Here C is item name starts with
-- Should compare with range (A-M) and return first row.
-- Condition column is varchar(3)
I have seen the solution on other way where range can be compared with input value, but here the range is present in the source table. Please help.
If I have understood what you are after correctly you can use
SELECT TOP 1 B.*
FROM TableB B
WHERE B.Name LIKE (SELECT CONCAT('[',condition,']%') FROM TableA WHERE ID =1)
ORDER BY B.Id
If I understand correctly, you should be structuring TableA as:
ID value Lower Upper
1 20 A M
2 50 N Z
Then you want:
select a.*
from tableA a
where left(#name, 1) between a.lower and a.upper;
You can get this to work with your format, by doing:
select a.*
from tableA a
where left(#name, 1) between left(a.condition) and right(a.condition);
But I don't recommend that. Better to store the condition in two columns.
I would use QUOTENAME() function as
SELECT *
FROM TableA
WHERE #Condition LIKE QUOTENAME(Condition);
This will be as
WHERE 'C' LIKE [A-M] --return True
Demo1
Demo2
Always you should try to add data and DDL for setup correctly the test scenario, here my proposed solution:
DECLARE #SourceA AS TABLE
(
ID INT,
Value INT,
Condition VARCHAR(100)
);
INSERT INTO #SourceA ( ID ,
Value ,
Condition
)
VALUES ( 1 , -- ID - int
110 , -- Value - int
'A-M' -- Condition - varchar(100)
),(2,250,'N-Z')
DECLARE #Alphabet VARCHAR(200)='A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z';
; WITH MyCTE AS
(
SELECT ID,Value,Condition, SUBSTRING(#Alphabet, PATINDEX('%'+ LEFT(Condition,1) + '%' ,#Alphabet),(LEN(#Alphabet)-PATINDEX('%'+ RIGHT(Condition,1) + '%' ,#Alphabet))+1) AS FormattedCondition
FROM #SourceA
)
SELECT * FROM MyCTE
WHERE MyCTE.FormattedCondition LIKE '%C%'

MS SQL does not return the expected top row when ordering by DIFFERENCE()

I have noticed strange behaviour in some SQL code used for address matching at the company I work for & have created some test SQL to illustrate the issue.
; WITH Temp (Id, Diff) AS (
SELECT 9218, 0
UNION
SELECT 9219, 0
UNION
SELECT 9220, 0
)
SELECT TOP 1 * FROM Temp ORDER BY Diff DESC
Returns 9218 but
; WITH Temp (Id, Name) AS (
SELECT 9218, 'Sonnedal'
UNION
SELECT 9219, 'Lammermoor'
UNION
SELECT 9220, 'Honeydew'
)
SELECT TOP 1 *, DIFFERENCE(Name, '') FROM Temp ORDER BY DIFFERENCE(Name, '') DESC
returns 9219 even though the Difference() is 0 for all records as you can see here:
; WITH Temp (Id, Name) AS (
SELECT 9218, 'Sonnedal'
UNION
SELECT 9219, 'Lammermoor'
UNION
SELECT 9220, 'Honeydew'
)
SELECT *, DIFFERENCE(Name, '') FROM Temp ORDER BY DIFFERENCE(Name, '') DESC
which returns
9218 Sonnedal 0
9219 Lammermoor 0
9220 Honeydew 0
Does anyone know why this happens? I am writing C# to replace existing SQL & need to return the same results so I can test that my code produces the same results. But I can't see why the actual SQL used returns 9219 rather than 9218 & it doesn't seem to make sense. It seems it's down to the Difference() function but it returns 0 for all the record in question.
When you call:
SELECT TOP 1 *, DIFFERENCE(Name, '')
FROM Temp l
ORDER BY DIFFERENCE(Name, '') DESC
All three records have a DIFFERENCE value of zero, and hence SQL Server is free to choose from any of the three records for ordering. That is to say, there is no guarantee which order you will get. The same is true for your second query. Actually, it is possible that the ordering for the same query could even change over time. In practice, if you expect a certain ordering, you should provide exact logic for it, e.g.
SELECT TOP 1 *
FROM Temp
ORDER BY Id;

SQL: I want a row to be return with NULL even if there is no match to my IN clause

I would like my SQL query to return a row even if there is no row matching in my IN clause.
For exemple this query:
SELECT id, foo
FROM table
WHERE id IN (0, 1, 2, 3)
would return:
id|foo
0|bar
1|bar
2|bar
3|null
But instead I have (because no row with id 3):
id|foo
0|bar
1|bar
2|bar
I have been able to find this trick:
SELECT tmpTable.id, table.bar
FROM (
SELECT 0 as id
UNION SELECT 1
UNION SELECT 2
UNION SELECT 3
) tmpTable
LEFT JOIN
(
SELECT table.foo, table.id
FROM table
WHERE table.id IN (0, 1, 2, 3)
) table
on table.id = tmpTable.id
Is there a better way?
Bonus: How to make it work with myBatis's list variable?
overslacked is right. Most SQL developers use an auxiliary table that stores integers (and one that stores dates). This is outlined in an entire chapter of Joe Celko's "SQL for Smarties".
Example:
CREATE TABLE numeri ( numero INTEGER PRIMARY KEY )
DECLARE #x INTEGER
SET #x = 0
WHILE #x < 1000
BEGIN
INSERT INTO numeri ( numero ) VALUES ( #x )
SET #x = #x + 1
END
SELECT
numero AS id,
foo
FROM
numeri
LEFT OUTER JOIN my_table
ON my_table.id = numero
WHERE
numero BETWEEN 0 AND 3
Main Goal of Programming minimal code high performance no need this things just remove id 3 from in clause
What about just saying:
SELECT id, foo
FROM table
WHERE id >= 0 AND <= 3

return a default record from a sql query

I have a sql query that I run against a sql server database eg.
SELECT * FROM MyTable WHERE Id = 2
This may return a number of records or may return none. If it returns none, I would like to alter my sql query to return a default record, is this possible and if so, how? If records are returned, the default record should not be returned. I cannot update the data so will need to alter the sql query for this.
Another way (you would get an empty initial rowset returned);
SELECT * FROM MyTable WHERE Id = 2
IF (##ROWCOUNT = 0)
SELECT ...
SELECT TOP 1 * FROM (
SELECT ID,1 as Flag FROM MyTable WHERE Id = 2
UNION ALL
SELECT 1,2
) qry
ORDER BY qry.Flag ASC
You can have a look to this post. It is similar to what you are asking
Return a value if no rows are found SQL
I hope that it can guide you to the correct path.
if not exists (SELECT top 1 * FROM mytable WHERE id = 2)
select * from mytable where id= 'whatever_the_default_id_is'
else
select * from mytable where id = 2
If you have to return whole rows of data (and not just a single column) and you have to create a single SQL query then do this:
Left join actual table to defaults single-row table
select
coalesce(a.col1, d.col1) as col1,
coalesce(a.col2, d.col2) as col2,
...
from (
-- your defaults record
select
default1 as col1,
default2 as col2,
...) as d
left join actual as a
on ((1 = 1) /* or any actual table "where" conditions */)
The query need to return the same number of fields, so you shouldn't do a SELECT * FROM but a SELECT value FROM if you want to return a default value.
With that in mind
SELECT value FROM MyTable WHERE Id = 2
UNION
SELECT CASE (SELECT count(*) FROM MyTable WHERE Id = 2)
WHEN 0 THEN 'defaultvalue'
END