SQL Server : How to make a Insert Into with Condition - sql

How is the syntax to make inserts with conditions? For example: verify if the value I want to insert isn't already in the table? (considering that the column could be null)

Your description is very brief but it sounds like you want the MERGE statement.
http://technet.microsoft.com/en-us/library/bb510625.aspx
Which can be used to insert/update/delete based on if data exists or not all in one statement.

Supposing values you want to insert are variables.
You can use IF NOT EXISTS:
IF NOT EXISTS (SELECT * FROM Table1 WHERE Col1 = #Val1 AND Col2 = #Val2)
INSERT INTO Table1 (Col1, COl2) VALUES (#Val1, #Val2)
or you can use SELECT..WHERE with EXISTS
INSERT INTO Table1 (Col1, COl2)
SELECT #Val1, #Val2
WHERE NOT EXISTS (SELECT * FROM Table1 WHERE Col1 = #Val1 AND Col2 = #Val2)
or probably few more methods (NOT IN, LEFT JOIN, MERGE...)

You could use a left or right join e.g.
WITH SourceTable AS
(
SELECT
*
FROM ( VALUES
('A', 1)
,('A', 2)
,('B', 1)
,('C', 10)) nTable(nCol1, nCol2)
)
SELECT
*
INTO #SourceTable
FROM SourceTable;
WITH NewRows AS
(
SELECT
*
FROM ( VALUES
('A', 2)
,('A', 3)
,('B', 1)
,('C', 11)) nTable(nCol1, nCol2)
)
INSERT #SourceTable
(nCol1
,nCol2)
SELECT
n.nCol1
,n.nCol2
FROM #SourceTable s
RIGHT JOIN NewRows n
ON s.nCol1=n.nCol1
AND s.nCol2=n.nCol2
WHERE s.nCol1 IS NULL;

Do you mean something like this?
insert into t(col)
select 'value'
where not exists (select 1 from t where col = 'value');
However, I would recommend that you use a unique index, filtered index, or foreign key constraint to maintain data integrity.

Try this:
insert into target_table (col1, col2, ...)
select col1, col2, ...
from source_table
where col_x not in (
select col_y from target_tabke where col_y is not null)
or (col_x is null and not exists (
select * from target_table where col_y is null))

Related

migration script sql server for inserting an item

I am trying to create an Migration Script but i need some help:, i had the very basic script to insert an item into a table but i am trying to do it in a way to check for the item first if it exists, then insert it else skip it
here is my code:
use mydatabase;
INSERT INTO dbo.mytable(col1,col2,col3)
SELECT '3','test1','test3/abc.html';
You don't need to repeat the expression just use value construct like that :
INSERT INTO dbo.mytable(col1,col2,col3)
SELECT t.col1, t.col2, t.col3
FROM (values (3, 'test1','test3/abc.html')) t (col1, col2, col3)
WHERE NOT EXISTS (
SELECT 1
FROM dbo.mytable m
WHERE m.col1 = t.col1
AND m.col2 = t.col2
AND m.col3 = t.col3
);
INSERT INTO dbo.mytable(col1,col2,col3)
SELECT '3','test1','test3/abc.html'
WHERE NOT EXISTS (
SELECT 1
FROM dbo.mytable
WHERE col1='3'
AND col2='test1'
AND col3='test3/abc.html'
)
You can change the where depending on what you consider already inserted.
Something like this (but you would need some kind of key to identify if the record exists or not) -
IF NOT EXISTS(SELECT 1 FROM dbo.mytable WHERE col1 = '3' AND col2 = 'test1' AND col3 = 'test3/abc.html')
BEGIN
insert into dbo.mytable(col1,col2,col3)
SELECT '3','test1','test3/abc.html'
END
Use a MERGE:
CREATE TABLE #mytable (col1 nvarchar(20), col2 nvarchar(20), col3 nvarchar(20))
INSERT INTO #mytable(col1,col2,col3)
SELECT '3','test1','test3/abc.html';
Merge #mytable t
USING (SELECT '3','test1','test3/abc.html') s (col1, col2, col3)
ON t.col1 = s.col1
AND t.col2 = s.col2
AND t.col3 = s.col3
when not matched by target then
INSERT (col1,col2,col3) VALUES (s.col1, s.col2, s.col3);

Select rows with same data in columns

I have a SQL Server database with a table with 20 columns. These columns have data as agree or disagree. Now I want to show rows in these columns which have "agree" data in them. I can use where clause but it is a time consuming task for 20 columns. I am looking for a SQL query which does this task.
You can use in:
select t.*
from t
where 'agree' in (col1, col2, ... col20);
There is no shortcut for this type of scenarios, If you want to compare all the columns, you have to explicitly mention each and every column like .
WHERE Col1='agree' AND Col2="agree"....
To avoid coding, you may go with dynamic query creation or creating a function, but ultimately it will be executed as same query comparing all the columns.
What about JOIN ?
If u have such a complex logic, best practices advise to keep data in different tables.
Here's some simplified example code that tests out several methods to return records that may or may not agree.
Just for the fun of it actually.
declare #T table (id int identity(1,1) primary key, col1 varchar(30), col2 varchar(30), col3 varchar(30));
insert into #T (col1, col2, col3) values
('agree','agree','agree'),
('agree','disagree','disagree'),
('agree','disagree',null),
('disagree','disagree','disagree'),
('disagree','disagree',null),
(null,null,null);
select 'OR' as method, * from #T
where (col1='agree' OR col2='agree' OR col3='agree');
select 'AND' as method, * from #T
where (col1='agree' AND col2='agree' AND col3='agree');
select 'IN' as method, * from #T
where 'agree' IN (col1, col2, col3);
select 'NOT IN' as method, * from #T
where 'agree' NOT IN (col1, col2, col3);
select 'LIKE' as method, * from #T
where CONCAT('-',col1,'-',col2,'-',col3,'-') LIKE '%-agree-%';
select 'NOT LIKE' as method, * from #T
where CONCAT('-',col1,'-',col2,'-',col3,'-') NOT LIKE '%-agree-%';
select 'ALL' as method, * from #T
where 'agree' = ALL(select col from (values (col1),(col2),(col3))q(col));
select 'SOME' as method, * from #T
where 'agree' = SOME(select col from (values (col1),(col2),(col3))q(col));
select 'ANY' as method, * from #T
where 'agree' = ANY(select col from (values (col1),(col2),(col3))q(col));
select 'EXISTS' as method, * from #T
where EXISTS (
select 1
from (values (col1),(col2),(col3))q(col)
where col = 'agree'
);
select 'NOT EXISTS' as method, * from #T
where NOT EXISTS (
select 1
from (values (col1),(col2),(col3))q(col)
where col = 'agree'
);

SQL - Concat in where clause with IN

I'm trying use IN to query multiple columns. If I use "=", I return rows (see example) but I would like to query multiple.
need query table to return rows A12345 and B98765 but not C00000
column1 | column2
A 12345
B 98765
C 00000
This works
SELECT *
FROM TABLE
WHERE (column1,column2) = ('A',12345)
This does not work.
SELECT *
FROM TABLE
WHERE (column1,column2) IN (('A',12345),('B',98765))
Here is error:
Error: SQL0104N An unexpected token "," was found following ",". Expected tokens may include: "AT MICROSECONDS MICROSECOND SECONDS SECOND MINUTES MINUTE HOURS". SQLSTATE=42601
(State:42601, Native Code: FFFFFF98)
I've tried several variations of parenthesis, commas's, etc and can't get it to work. Is it possible to do this and if so can you provide the syntax.
thanks
DB2 9.7 onwards:
SELECT *
FROM TABLE
WHERE (column1, column2) IN (VALUES ('A', 12345), ('B', 98765))
One solution is to use a temp table:
create table #tmp
(
col1 varchar(2),
col2 int
);
insert into #tmp (col1, col2)
values ('A', 12345), ('B', 98765)
SELECT t.* FROM TABLE t
JOIN #tmp ON #tmp.col1 = t.column1 and #tmp.col2 = t.column2
You haven't stated your RDBMS, but have you tried:
SELECT * FROM TABLE
WHERE (column1, column2) IN ('A', 12345)
OR (column1, column2) IN ('B', 98765)
Depending on your data, this might be workable:
SELECT *
FROM TABLE T
JOIN (
SELECT 'A' COL1, 12345 COL2
UNION ALL
SELECT 'B', 98765
UNION ALL
SELECT 'C', 44365) AS Matches
ON T.column1 = Matches.COL1
AND T.column2 = Matches.COL2
This turned out to be easy and should have known this.
SELECT *
FROM TABLE
WHERE CONCAT(column1, column2) IN ('A12345','B98765')

Create enumeration and use it in aggregate function

Is it possible to create some enumaration for 'a','b','test','123','blabla' in following statement?
sum(case when col1 in ('a','b','test','123','blabla') then col2 end) as sum
I've tried to read it from letters_table like this:
sum(case when col1 in (select letter from letters_table) then col2 end) as sum
but it told me Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
and following is not working fine for me:
DECLARE #letters varchar(10)
select #letters = letter FROM letters_table
sum(case when col1 in (#letters) then col2 end) as sum
because when I print the #letters, there is just the last one 'blabla'
If you have col1 (a text column) and col2 (a numeric column) in a table, say myTable and letter (a text column) in another table, say letters_table, then you can use a JOIN, as below:
SELECT
--mt.col1, Include for totals by col1
SUM(mt.col2)
FROM myTable mt
INNER JOIN letters_table lt
ON mt.col1 = lt.letter;
-- GROUP BY mt.col1 Include for totals by col1
-- ORDER BY mt.col1; Include for totals by col1
Your second example using the subquery is probably the best - you just need to do the subquery and aggregation in two separate phases, like this:
select sum(x.col2) as sum
from (
select case
when col1 in (select letter from letters_table) then col2
else 0
end as col2
from YourTableName
) x
This should work as intended, and make the parser happy.
...or you could use a Common Table Expression, for example:
DECLARE #Test TABLE (col1 VARCHAR(1), col2 INT);
INSERT INTO #Test VALUES ('a', 1);
INSERT INTO #Test VALUES ('c', 20);
INSERT INTO #Test VALUES ('z', 3);
DECLARE #Letters TABLE (Letter VARCHAR(1));
INSERT INTO #Letters VALUES ('a');
INSERT INTO #Letters VALUES ('b');
INSERT INTO #Letters VALUES ('c');
WITH PreQuery AS (
SELECT
col1,
CASE WHEN l.Letter IS NULL THEN 0 ELSE 1 END AS GoodLetter,
col2
FROM
#Test t
LEFT JOIN #Letters l ON l.Letter = t.col1)
SELECT
SUM(CASE WHEN GoodLetter = 1 THEN col2 END)
FROM
PreQuery;
The solution posted by Richard should do the trick, but there is no need to use cte
you can simply use the code below
select
isnull(sum(mt.col2),0)
from
myTbl mt
inner join lettersTbl lt
on mt.col1 = lt.letter
If you want to include the letters involved in the results, you may use a variable. Looking at your previous post here is a code with the same sample data that you have provided
declare #yourTbl table (col1 char(1), col2 int);
insert into #yourTbl values ('a', 10)
,('b', 5)
,('c', 15)
,('d', 2)
,('a', 3)
,('b', 6)
,('c', 8)
,('d', 10);
declare #lettersTbl table (letter char(1));
-- insert your letters here
insert into #lettersTbl values ('a'),('b')
-- only needed if you want to display the letters involved too
declare #checkedLetters as varchar(50)
select #checkedLetters = concat(#checkedletters,lt.letter)
from
#letterstbl lt
select
#checkedletters
,isnull(sum(mt.col2),0)
from
#yourtbl mt
inner join #letterstbl lt
on mt.col1 = lt.letter

Select only distinct values from two columns from a table

If I have a table such as
1 A
1 B
1 A
1 B
2 C
2 C
And I want to select distinct from the two columns so that I would get
1
2
A
B
C
How can I word my query? Is the only way to concatenate the columns and wrap them around a distinct function operator?
You could use a union to create a table of all values from both columns:
select col1 as BothColumns
from YourTable
union
select col2
from YourTable
Unlike union all, union removes duplicates, even if they come from the same side of the union.
SQL Fiddle
Why even distinct in Union, try this :
select cast(id as char(1)) from test
union
select val from test
Please try:
Select Col1 from YourTable
union
Select Col2 from YourTable
UNION removes duplicate records (where all columns in the results are the same), UNION ALL does not.
Please check What is the difference between UNION and UNION ALL
For multiple columns, you can go for UNPIVOT.
SELECT distinct DistValues
FROM
(SELECT Col1, Col2, Col3
FROM YourTable) p
UNPIVOT
(DistValues FOR Dist IN
(Col1, Col2, Col3)
)AS unpvt;
Try this one -
DECLARE #temp TABLE
(
Col1 INT
, Col2 NVARCHAR(50)
)
INSERT INTO #temp (Col1, Col2)
VALUES (1, 'ab5defg'), (2, 'ae4eii')
SELECT disword = (
SELECT DISTINCT dt.ch
FROM (
SELECT ch = SUBSTRING(t.mtxt, n.number + 1, 1)
FROM [master].dbo.spt_values n
CROSS JOIN (
SELECT mtxt = (
SELECT CAST(Col1 AS VARCHAR(10)) + Col2
FROM #temp
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'
)
) t
WHERE [type] = N'p'
AND number <= LEN(mtxt) - 1
) dt
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'
)
Or try this -
DECLARE #temp TABLE
(
a CHAR(1), b CHAR(1)
)
INSERT INTO #temp (a, b)
VALUES
('1', 'A'), ('1', 'B'), ('1', 'A'),
('1', 'B'), ('2', 'C'), ('2', 'C')
SELECT a
FROM #temp
UNION
SELECT b
FROM #temp
Because what you want select is in different columns, you can use union like below:
select distinct tarCol from
(select distinct column1 as tarCol from table
union
select distinct column2 from table) as tarTab
You can use like this to get multiple distinct column values
(SELECT DISTINCT `enodeb` as res,
"enodeb" as columnname
FROM `raw_metrics`)
UNION
(SELECT DISTINCT `interval` as res,
"interval" as columnname
FROM `raw_metrics`)