When Merge null columns are not merged just inserted - sql

My table and it's data are below
CREATE TABLE Sales (Id int ITDENTITY(1,1) NOT NULL,stateid int, district int Sitecount int)
CREATE TABLE Sales1(stateid int, district int,
Sitecount Int)
insert into Sales values (1,2,12)
insert into Sales values (1,3,20)
insert into Sales values (1, NULL, 10)
insert into Salesi values (1,2,10)
insert into Salesi values (1,2. 100)
insert into Select values (1,ULL, 18)
I have used the below query to merge
MERGE Sales AS T
USING (Select stateid, district, Sitecount from Sales1 group by stateid,district) as S
ON(S.stateid =T.stateld and S.district=T.district)
WHEN MATCHED
Then UPDATE SET
T.Sitecount=T.Sitecount+S.Sitecount
WHEN NOT MATCHED BY TARGET THEN INSERT (stateid,district,Sitecount) VALUES(S stateid, S.district, S.5itecount);
Whenever I run the query, if the matched data all columns are not null then only data merged,
Otherwise it is inserted as a new row.
If district data is null, need to add the sitecount based on the stateid.How to achieve it. Suggest me..

You can match on stateid using ISNULL in join like below:
MERGE Sales AS T
USING (Select stateid, district, max(Sitecount) Sitecount from Sales1 group by stateid,district) as S
ON(S.stateid =T.stateid and isnull(S.district,'')=isnull(T.district, ''))
WHEN MATCHED
Then UPDATE SET
T.Sitecount=T.Sitecount+S.Sitecount
WHEN NOT MATCHED BY TARGET THEN INSERT (stateid,district,Sitecount) VALUES(S.stateid, S.district, S.Sitecount);
select * from Sales
Also, please note that I have used max(Sitecount) to avoid the duplicates in join. Please change as per your requirement.
Please find the db<>fiddle here.

Try this:
MERGE Sales AS T
USING (Select stateid, district, Sitecount from Sales1 group by stateid,district) as S
ON(ISNULL(S.stateid, -1) = ISNULL(T.stateld, -1) and ISNULL(S.district, '')= ISNULL(T.district, ''))
WHEN MATCHED
Then UPDATE SET
T.Sitecount=T.Sitecount+S.Sitecount
WHEN NOT MATCHED BY TARGET THEN INSERT (stateid,district,Sitecount) VALUES(S stateid, S.district, S.5itecount);
The following element was adjusted to isnull the comparison fields as a null value as you have observed are treated differently and incorrectly (for what you desire) execute the insert element:
ON(ISNULL(S.stateid, -1) = ISNULL(T.stateld, -1) and ISNULL(S.district, '')= ISNULL(T.district, ''))

Related

Insert into table from select only when select returns valid rows

I want to insert into table from select statement but it is required that insert only happens when select returns valid rows. If no rows return from select, then no insertion happens.
insert into products (name, type) select 'product_name', type from prototype where id = 1
However, the above sql does insertion even when select returns no rows.
It tries to insert NULL values.
I know the following sql can check if row exists
select exists (select true from prototype where id = 1)
How to write a single SQL to add the above condition to insert to exclude the case ?
You are inserting the wrong way. See the example below, that doesn't insert any row since none matches id = 1:
create table products (
name varchar(10),
type varchar(10)
);
create table prototype (
id int,
name varchar(10),
type varchar(10)
);
insert into prototype (id, name, type) values (5, 'product5', 'type5');
insert into prototype (id, name, type) values (7, 'product7', 'type7');
insert into products (name, type) select name, type from prototype where id = 1
-- no rows were inserted.

How to update a temporary table with a value from a select statement?

I have created a temporary table to hold some values like this:
CREATE TABLE #TempCount (PendingOrders INT, OpenOrders INT, ClosedOrders INT);
I want to update the columns with a value from a SELECT statement as such:
UPDATE #TempCount
SET PendingOrders =
(
SELECT
COUNT(OrderID) AS 'PendingOrders'
FROM
dbo.Products
WHERE
OrderStatus = 1
)
However nothing seems to get updated. I expected PendingOrders to show a number but it doesn't get anything entered in. The column is empty. The same applies to the other columns - nothing gets updated.
How could I solve this?
You need a row in order to update it. Do one "initializing" INSERT first:
INSERT INTO #TempCount VALUES(0, 0, 0)
Seems to me like you need an insert statement first to generate the row.
INSERT #TempCount (PendingOrders)
SELECT COUNT(OrderID) AS 'PendingOrders'
FROM dbo.Products
WHERE OrderStatus = 1
If you just want to hold single values then consider variables
declare #PendingOrders int
set #PendingOrders = ( SELECT COUNT(OrderID)
FROM dbo.Products
WHERE OrderStatus = 1 )
select #PendingOrders, #OpenOrders, #ClosedOrders
Use this syntax:
UPDATE t
SET t.xxx=p.xxx
FROM #TempCount t, Products p
WHERE ......
Subselect is not always necessary.

Filtering a group of records

Please see the SQL structure below:
CREATE table TestTable (id int not null identity, [type] char(1), groupid int)
INSERT INTO TestTable ([type]) values ('a',1)
INSERT INTO TestTable ([type]) values ('a',1)
INSERT INTO TestTable ([type]) values ('b',1)
INSERT INTO TestTable ([type]) values ('b',1)
INSERT INTO TestTable ([type]) values ('a',2)
INSERT INTO TestTable ([type]) values ('a',2)
The first four records are part of group 1 and the fifth and sixth records are part of group 2.
If there is at least one b in the group then I want the query to only return b's for that group. If there are no b's then the query should return all records for that group.
Here you go
SELECT *
FROM testtable
LEFT JOIN (SELECT distinct groupid FROM TestTable WHERE type = 'b'
) blist ON blist.groupid = testtable.groupid
WHERE (blist.groupid = testtable.groupid and type = 'b') OR
(blist.groupid is null)
How it works
join to a list of items that contain b.
Then in where statement... if we exist in that list just take type b. Otherwise take everything.
As an after-note you could be cute with the where clause like this
WHERE ISNULL(blist.groupid,testtable.groupid) = testtable.groupid
I think this is less clear -- but is often how advanced users will do it.

Merge Statement with two inserts?

Given is a simple MERGE statement. Where I Insert/Update records into traget table. Question: is it possible to also Insert those values in another table with a flag beeing 0 for insert and 1 for update? Eg. when not match do insert into target and another table, when matched do update target and insert into another table.
MERGE dbo.FactBuyingHabits AS Target
USING (SELECT CustomerID, ProductID, PurchaseDate FROM dbo.Purchases) AS Source
ON (Target.ProductID = Source.ProductID AND Target.CustomerID = Source.CustomerID)
WHEN MATCHED THEN
UPDATE SET Target.LastPurchaseDate = Source.PurchaseDate
--and insert into test_tbl values (1, Source.ProductID, Source.CustomerID) --?
WHEN NOT MATCHED BY TARGET THEN
INSERT (CustomerID, ProductID, LastPurchaseDate)
VALUES (Source.CustomerID, Source.ProductID, Source.PurchaseDate)
--and insert into test_tbl values (0, Source.ProductID, Source.CustomerID) --?
you should read about OUTPUT
ex (source);
DECLARE #MergeOutput1 table
(
ActionType nvarchar(10),
BookID int,
OldBookTitle nvarchar(50),
NewBookTitle nvarchar(50),
ModifiedDate datetime
);
-- use MERGE statement to perform update on Book2
MERGE Books2 AS b2
USING Books AS b1
ON (b2.BookID = b1.BookID)
WHEN MATCHED
THEN UPDATE
SET b2.BookTitle = b1.BookTitle
OUTPUT
$action,
INSERTED.BookID,
DELETED.BookTitle,
INSERTED.BookTitle,
INSERTED.ModifiedDate
INTO #MergeOutput1;

Select records with order of IN clause

I have
SELECT * FROM Table1 WHERE Col1 IN(4,2,6)
I want to select and return the records with the specified order which i indicate in the IN clause
(first display record with Col1=4, Col1=2, ...)
I can use
SELECT * FROM Table1 WHERE Col1 = 4
UNION ALL
SELECT * FROM Table1 WHERE Col1 = 6 , .....
but I don't want to use that, cause I want to use it as a stored procedure and not auto generated.
I know it's a bit late but the best way would be
SELECT *
FROM Table1
WHERE Col1 IN( 4, 2, 6 )
ORDER BY CHARINDEX(CAST(Col1 AS VARCHAR), '4,2,67')
Or
SELECT CHARINDEX(CAST(Col1 AS VARCHAR), '4,2,67')s_order,
*
FROM Table1
WHERE Col1 IN( 4, 2, 6 )
ORDER BY s_order
You have a couple of options. Simplest may be to put the IN parameters (they are parameters, right) in a separate table in the order you receive them, and ORDER BY that table.
The solution is along this line:
SELECT * FROM Table1
WHERE Col1 IN(4,2,6)
ORDER BY
CASE Col1
WHEN 4 THEN 1
WHEN 2 THEN 2
WHEN 6 THEN 3
END
select top 0 0 'in', 0 'order' into #i
insert into #i values(4,1)
insert into #i values(2,2)
insert into #i values(6,3)
select t.* from Table1 t inner join #i i on t.[in]=t.[col1] order by i.[order]
Replace the IN values with a table, including a column for sort order to used in the query (and be sure to expose the sort order to the calling application):
WITH OtherTable (Col1, sort_seq)
AS
(
SELECT Col1, sort_seq
FROM (
VALUES (4, 1),
(2, 2),
(6, 3)
) AS OtherTable (Col1, sort_seq)
)
SELECT T1.Col1, O1.sort_seq
FROM Table1 AS T1
INNER JOIN OtherTable AS O1
ON T1.Col1 = O1.Col1
ORDER
BY sort_seq;
In your stored proc, rather than a CTE, split the values into table (a scratch base table, temp table, function that returns a table, etc) with the sort column populated as appropriate.
I have found another solution. It's similar to the answer from onedaywhen, but it's a little shorter.
SELECT sort.n, Table1.Col1
FROM (VALUES (4), (2), (6)) AS sort(n)
JOIN Table1
ON Table1.Col1 = sort.n
I am thinking about this problem two different ways because I can't decide if this is a programming problem or a data architecture problem. Check out the code below incorporating "famous" TV animals. Let's say that we are tracking dolphins, horses, bears, dogs and orangutans. We want to return only the horses, bears, and dogs in our query and we want bears to sort ahead of horses to sort ahead of dogs. I have a personal preference to look at this as an architecture problem, but can wrap my head around looking at it as a programming problem. Let me know if you have questions.
CREATE TABLE #AnimalType (
AnimalTypeId INT NOT NULL PRIMARY KEY
, AnimalType VARCHAR(50) NOT NULL
, SortOrder INT NOT NULL)
INSERT INTO #AnimalType VALUES (1,'Dolphin',5)
INSERT INTO #AnimalType VALUES (2,'Horse',2)
INSERT INTO #AnimalType VALUES (3,'Bear',1)
INSERT INTO #AnimalType VALUES (4,'Dog',4)
INSERT INTO #AnimalType VALUES (5,'Orangutan',3)
CREATE TABLE #Actor (
ActorId INT NOT NULL PRIMARY KEY
, ActorName VARCHAR(50) NOT NULL
, AnimalTypeId INT NOT NULL)
INSERT INTO #Actor VALUES (1,'Benji',4)
INSERT INTO #Actor VALUES (2,'Lassie',4)
INSERT INTO #Actor VALUES (3,'Rin Tin Tin',4)
INSERT INTO #Actor VALUES (4,'Gentle Ben',3)
INSERT INTO #Actor VALUES (5,'Trigger',2)
INSERT INTO #Actor VALUES (6,'Flipper',1)
INSERT INTO #Actor VALUES (7,'CJ',5)
INSERT INTO #Actor VALUES (8,'Mr. Ed',2)
INSERT INTO #Actor VALUES (9,'Tiger',4)
/* If you believe this is a programming problem then this code works */
SELECT *
FROM #Actor a
WHERE a.AnimalTypeId IN (2,3,4)
ORDER BY case when a.AnimalTypeId = 3 then 1
when a.AnimalTypeId = 2 then 2
when a.AnimalTypeId = 4 then 3 end
/* If you believe that this is a data architecture problem then this code works */
SELECT *
FROM #Actor a
JOIN #AnimalType at ON a.AnimalTypeId = at.AnimalTypeId
WHERE a.AnimalTypeId IN (2,3,4)
ORDER BY at.SortOrder
DROP TABLE #Actor
DROP TABLE #AnimalType
ORDER BY CHARINDEX(','+convert(varchar,status)+',' ,
',rejected,active,submitted,approved,')
Just put a comma before and after a string in which you are finding the substring index or you can say that second parameter.
And first parameter of CHARINDEX is also surrounded by , (comma).