How to insert using different table based on condition in same query - sql

I am merging data in one table from tables of 2 database. Structure is as per below:
Table in new Database :
User Table : {UserName,Email}
Table in Database1 :
User Table : {UserName,Email,LastLogin}
Table in Database2 :
User Table : {UserName,Email,LastLogin}
Now i need to write query that if Email address are same in 2 tables from database 1 and database2 then we need to insert record where LastLogin is latest.
Can someone suggest over this.

I think you are in need of this.. :)
Try modifying it accordingly..
declare #Email_1 nvarchar(100),#Email_2 nvarchar(100),#UserName nvarchar(100),#Lastlogin_1 datetime,#Lastlogin_2 datetime,#loop int=0
use [Database1]
while #loop != (select count(Distinct Email) from [User Table])
BEGIN
use [Database1]
set #Email_1 = (select Distinct Email from [User Table] order by email asc offset #loop rows fetch next 1 rows only)
set #LastLogin_1 = (select top 1 max(LastLogin) from [User Table] where email=#Email_1)
use [Database2]
set #Email_2 = (select top 1 Email from [User Table] where Email like '%#Email_1%')
set #LastLogin_2 = (select top 1 max(LastLogin) from [User Table] where email=#Email_2)
if #email_1=#email_2
BEGIN
if #LastLogin_1>#LastLogin_2
BEGIN
use [Database_1]
set #username = (select top 1 Username from [user table] where email=#email_1 and lastlogin=#Lastlogin_1)
use [New Database]
insert into [User Table]
select #username,#email_1
END
else if #LastLogin_1<#LastLogin_2
BEGIN
use [Database_2]
set #username = (select top 1 Username from [user table] where email=#email_2 and lastlogin=#Lastlogin_2)
use [New Database]
insert into [User Table]
select #username,#email_1
use [Database1]
END
END
set #loop=#loop+1
END

My following code are assuming all tables are in one database, this is more for demo convenience purpose. In real world, when you have tables in different databases, then you need to use 3-part naming convention, i.e.
[DB].[Schema].[Table]
Also I am testing in sql server environment.
use tempdb
drop table dbo.merge_tbl, dbo.t1, dbo.t2;
create table dbo.merge_tbl (username varchar(30), email varchar(30));
create table dbo.t1 (username varchar(30), email varchar(30), lastlogin datetime)
create table dbo.t2 (username varchar(30), email varchar(30), lastlogin datetime)
go
-- insert a few records to tables
insert into dbo.t1 (username, email, lastlogin)
values ('james1', 'j#a.com', '20161001'), ('jenny1', 'j2#b.com', '20161002'), ('jeffrey1', 'j3#c.com', '20150101')
insert into dbo.t2(username, email, lastlogin)
values ('james2', 'j#a.com', '20161006'), ('jenny2', 'j2#b.com', '20151002'), ('jeffrey2', 'j4#c.com', '20170101')
go
-- this is the insert statement
insert into dbo.merge_tbl (username, email)
select case when t1.lastlogin >= t2.lastlogin then t1.username else t2.username end
, case when t1.lastlogin >= t2.lastlogin then t1.email else t2.email end
from dbo.t1
inner join dbo.t2
on t1.email = t2.email;
go
-- check result
select * from dbo.merge_tbl
Here is the result

follow 3 part naming convention([Database Name].[Schema].[Table name]) while accessing your table in different database. Also there may be multiple log in entries for a single user in the table [User Table] ,so use grouping function in such scenario.
If you are using SQL Server,use the below script for achieving the result.
INSERT INTO [New Database].dbo.[User Table] (UserName,Email)
SELECT CASE WHEN MAX(a.LastLogin)>=MAX(b.LastLogin) THEN a.[UserName] ELSE b.[UserName] END [UserName],a.Email
FROM Database1.dbo.[User Table] a
JOIN Database2.dbo.[User Table] b
ON a.Email=b.Email
GROUP BY a.[UserName],b.[UserName],a.Email

Assuming the three database db0, db1 and db2. For merging data in database db0 run the following query:
insert into db0.user('name','email')
select name, email from (
select db1.name,db1.email,
case
when db1.user.lastlogin >= db2.user.lastlogin
then db1.user.lastlogin
else db2.user.lastlogin
end as lastlogin
from db1.user,db2.user
where db1.user.email = db2.user.email ) as a
UPDATE
insert into db0.user(all columns)
select * from (
select "apply case for some columns" from db1.user,db2.user
where "apply your condition" ) as a
consider the following facts:
1. In this above query we are doing cross multiplication between db1.user and db2.user by which we get all possible values from these 2 tables
If you want to apply condition in "where" clause for email(for example db1.user.email = db2.user.email) then you can write db1.user.email or db2.user.email after "select", because both values are same.
If you want to apply >= or <= condition you have to apply "case" after "select".Because you have to fetch any one value from 2 tables
For example you want to fetch lastlogin data, for this condition I have applied case in lastlogin column. By this condition you will get column value from either db1.user or db2.user.
conditions in where clause can be written in case form after "select" also.
Please have a look on the UPDATE portion.
And I went for prayer, that is why this late reply.
If it helps mark as right answer.
Thank you

Related

Select all values in 1 view ( can't use procedure and temp tables)

I need to create a View with select in my database. It must be view can't use stored procedure so it needs to be created in just one query without temp tales.
I create a simple example:
CREATE TABLE #TemporaryTable -- Local temporary table - starts with single #
(
id int,
date_order date,
order_status varchar(50)
);
insert into #TemporaryTable
VALUES
('1','2022-01-01','Completed'),
('2','2022-01-01','Cancelled'),
('3','2022-01-01','Completed'),
('4','2022-01-01','Completed'),
('5','2022-01-02','Cancelled'),
('6','2022-01-02','Cancelled'),
('7','2022-01-02','Completed'),
('8','2022-01-02','Completed'),
('9','2022-01-02','Completed'),
('10','2022-01-03','Cancelled'),
('11','2022-01-04','Completed')
select * from #TemporaryTable
SELECT COUNT(crm1.date_order) AS [count_all_orders], crm1.date_order AS [date_order],COUNT(crm2.date_order) AS [[count_cancelled_orders]
FROM #TemporaryTable crm1 WITH (nolock)
left outer join #TemporaryTable crm2 WITH (nolock) on crm2.id = crm1.id and crm2.order_status ='Cancelled'
GROUP BY crm1.date_order,crm2.date_order
SQL fiddle
The endpoint is to have 1 date and count all orders + count cancelled orders.
Now the date is doubled and giving bad values. Please check that in SQL fiddle and help me.
Thank you!
There is no need to join, you can simply use conditional aggregation:
COUNT(someNonNullValue) and COUNT(*) are the same thing
SELECT
COUNT(*) AS count_all_orders,
crm1.date_order,
COUNT(CASE WHEN crm2.order_status = 'Cancelled' THEN 1 END) AS count_cancelled_orders
FROM #TemporaryTable crm
GROUP BY
crm.date_order;
NOLOCK is a very bad idea. Only use it in exceptional circumstances.

Inserting data into table variable

I want to know if it's possible to insert data into a table variable from a select query. When using a variable table before I have inserted the values and typed them myself.
I have come up with the following select query that displays the latest comment for a repair order. It returns 37 records and I wanted to know if it's possible to insert those into a variable table.
SELECT
a.CH_REPREF, a.CH_DATE, a.CH_CRTIME,
b.[Latest Customer Comment], a.CH_CRUSER, a.CH_CCOMMNT
FROM
(SELECT
CH_REPREF,
MAX(CH_REPREF1) As [Latest Customer Comment]
FROM
dbo.V_CSRPCH
WHERE
CH_CCOMMNT IS NOT NULL
AND CH_CCOMMNT NOT LIKE 'X'
GROUP BY
CH_REPREF) b,
(SELECT
CH_REPREF, CH_DATE, CH_CRTIME, CH_REPREF1,
CH_CRUSER, CH_CCOMMNT, CH_ACCOUNT
FROM
dbo.V_CSRPCH) a
WHERE
CH_REPREF1 = [Latest Customer Comment]
AND CH_ACCOUNT = 'DDCHC'
I have started the table variable with the following, but am stuck whether it's possible to fill it with the data returned by the select query:
Declare #tbl_last_customer_comment TABLE
(CH_REPREF nvarchar(10),
CH_DATE smalldatetime,
CH_CRTIME nvarchar(8),
Latest int,
CH_CRUSER nvarchar(8),
CH_CCOMMNT text)
just do it as 'constant' table-
INSERT INTO #tbl_last_customer_comment
SELECT a.CH_REPREF, a.CH_DATE, a.CH_CRTIME,b.[Latest Customer Comment],a.CH_CRUSER,a.CH_CCOMMNT
FROM (SELECT CH_REPREF, MAX(CH_REPREF1) As [Latest Customer Comment]
FROM dbo.V_CSRPCH
WHERE CH_CCOMMNT IS Not Null AND CH_CCOMMNT Not Like 'X'
GROUP BY CH_REPREF) b,
(SELECT CH_REPREF, CH_DATE, CH_CRTIME,CH_REPREF1, CH_CRUSER, CH_CCOMMNT, CH_ACCOUNT
FROM dbo.V_CSRPCH) a
WHERE CH_REPREF1 = [Latest Customer Comment] AND CH_ACCOUNT = 'DDCHC'

I am looking for a way for a trigger to insert into a second table only where the value in table 1 changes

I am looking for a way for a trigger to insert into a second table only where the value in table 1 changes. It is essentially an audit tool to trap any changes made. The field in table 1 is price and we want to write additional fields.
This is what I have so far.
CREATE TRIGGER zmerps_Item_costprice__update_history_tr ON [ITEM]
FOR UPDATE
AS
insert into zmerps_Item_costprice_history
select NEWID(), -- unique id
GETDATE(), -- CURRENT_date
'PRICE_CHANGE', -- reason code
a.ima_itemid, -- item id
a.ima_price-- item price
FROM Inserted b inner join item a
on b.ima_recordid = a.IMA_RecordID
The table only contains a unique identifier, date, reference(item) and the field changed (price). It writes any change not just a price change
Is it as simple as this? I moved some of the code around because comments after the comma between columns is just painful to maintain. You also should ALWAYS specify the columns in an insert statement. If your table changes this code will still work.
CREATE TRIGGER zmerps_Item_costprice__update_history_tr ON [ITEM]
FOR UPDATE
AS
insert into zmerps_Item_costprice_history
(
UniqueID
, CURRENT_date
, ReasonCode
, ItemID
, ItemPrice
)
select NEWID()
, GETDATE()
, 'PRICE_CHANGE'
, d.ima_itemid
, d.ima_price
FROM Inserted i
inner join deleted d on d.ima_recordid = i.IMA_RecordID
AND d.ima_price <> i.ima_price
Since you haven't provided any other column names I Have used Column2 and Column3 and the "Other" column names in the below example.
You can expand adding more columns in the below code.
overview about the query below:
Joined the deleted and inserted table (only targeting the rows that has changed) joining with the table itself will result in unnessacary processing of the rows which hasnt changed at all.
I have used NULLIF function to yeild a null value if the value of the column hasnt changed.
converted all the columns to same data type (required for unpivot) .
used unpivot to eliminate all the nulls from the result set.
unpivot will also give you the column name its has unpivoted it.
CREATE TRIGGER zmerps_Item_costprice__update_history_tr
ON [ITEM]
FOR UPDATE
AS
BEGIN
SET NOCOUNT ON ;
WITH CTE AS (
SELECT CAST(NULLIF(i.Price , d.Price) AS NVARCHAR(100)) AS Price
,CAST(NULLIF(i.Column2 , d.Column2) AS NVARCHAR(100)) AS Column2
,CAST(NULLIF(i.Column3 , d.Column3) AS NVARCHAR(100)) AS Column3
FROM dbo.inserted i
INNER JOIN dbo.deleted d ON i.IMA_RecordID = d.IMA_RecordID
WHERE i.Price <> d.Price
OR i.Column2 <> d.Column2
OR i.Column3 <> d.Column3
)
INSERT INTO zmerps_Item_costprice_history
(unique_id, [CURRENT_date], [reason code], Item_Value)
SELECT NEWID()
,GETDATE()
,Value
,ColumnName + '_Change'
FROM CTE UNPIVOT (Value FOR ColumnName IN (Price , Column2, Column3) )up
END
As I understand your question correctly, You want to record change If and only if The column Price value is changes, you dont need any other column changes to be recorded
here is your code
CREATE TRIGGER zmerps_Item_costprice__update_history_tr ON [ITEM]
FOR UPDATE
AS
if update(ima_price)
insert into zmerps_Item_costprice_history
select NEWID(), -- unique id
GETDATE(), -- CURRENT_date
'PRICE_CHANGE', -- reason code
a.ima_itemid, -- item id
a.ima_price-- item price
FROM Inserted b inner join item a
on b.ima_recordid = a.IMA_RecordID

Select rows and Update same rows for locking?

I need to write a procedure that will allow me to select x amount of rows and at the same time update those rows so the calling application will know those records are locked and in use. I have a column in the table named "locked". The next time the procedure is called it will only pull the next x amount of records that do not have the "locked" column checked. I have read a little about the OUTPUT method for SQL server, but not sure that is what I want to do.
As you suggested, you can use the OUTPUT clause effectively:
Live demo: https://data.stackexchange.com/stackoverflow/query/8058/so3319842
UPDATE #tbl
SET locked = 1
OUTPUT INSERTED.*
WHERE id IN (
SELECT TOP 1 id
FROM #tbl
WHERE locked = 0
ORDER BY id
)​
Also see this article:
http://www.sqlmag.com/article/tsql3/more-top-troubles-using-top-with-insert-update-and-delete.aspx
Vote for Cade Roux's answer, using OUTPUT:
UPDATE #tbl
SET locked = 1
OUTPUT INSERTED.*
WHERE id IN (SELECT TOP 1 id
FROM #tbl
WHERE locked = 0
ORDER BY id)​
Previously:
This is one of the few times I can think of using a temp table:
ALTER PROCEDURE temp_table_test
AS
BEGIN
SELECT TOP 5000 *
INTO #temp_test
FROM your_table
WHERE locked != 1
ORDER BY ?
UPDATE your_table
SET locked = 1
WHERE id IN (SELECT id FROM #temp_test)
SELECT *
FROM #temp_test
IF EXISTS (SELECT NULL
FROM tempdb.dbo.sysobjects
WHERE ID = OBJECT_ID(N'tempdb..#temp_test'))
BEGIN
DROP TABLE #temp_test
END
END
This:
Fetches the rows you want, stuffs them into a local temp table
Uses the temp table to update the rows to be "locked"
SELECTs from the temp table to give you your resultset output
Drops the temp table because they live for the session

Updating Uncommitted data to a cell with in an UPDATE statement

I want to convert a table storing in Name-Value pair data to relational form in SQL Server 2008.
Source table
Strings
ID Type String
100 1 John
100 2 Milton
101 1 Johny
101 2 Gaddar
Target required
Customers
ID FirstName LastName
100 John Milton
101 Johny Gaddar
I am following the strategy given below,
Populate the Customer table with ID values in Strings Table
INSERT INTO CUSTOMERS SELECT DISTINCT ID FROM Strings
You get the following
Customers
ID FirstName LastName
100 NULL NULL
101 NULL NULL
Update Customers with the rest of the attributes by joining it to Strings using ID column. This way each record in Customers will have corresponding 2 matching records.
UPDATE Customers
SET FirstName = (CASE WHEN S.Type=1 THEN S.String ELSE FirstName)
LastName = (CASE WHEN S.Type=2 THEN S.String ELSE LastName)
FROM Customers
INNER JOIN Strings ON Customers.ID=Strings.ID
An intermediate state will be llike,
ID FirstName LastName ID Type String
100 John NULL 100 1 John
100 NULL Milton 100 2 Milton
101 Johny NULL 101 1 Johny
101 NULL Gaddar 101 2 Gaddar
But this is not working as expected. Because when assigning the values in the SET clause it is setting only the committed values instead of the uncommitted. Is there anyway to set uncommitted values (with in the processing time of query) in UPDATE statement?
PS: I am not looking for alternate solutions but make my approach work by telling SQL Server to use uncommitted data for UPDATE.
The easiest way to do it would be to split the update into two:
UPDATE Customers
SET FirstName = Strings.String
FROM Customers
INNER JOIN Strings ON Customers.ID=Strings.ID AND Strings.Type = 1
And then:
UPDATE Customers
SET LastName = Strings.String
FROM Customers
INNER JOIN Strings ON Customers.ID=Strings.ID AND Strings.Type = 2
There are probably ways to do it in one query such as a derived table, but unless that's a specific requirement I'd just use this approach.
Have a look at this, it should avoid all the steps you had
DECLARE #Table TABLE(
ID INT,
Type INT,
String VARCHAR(50)
)
INSERT INTO #Table (ID,[Type],String) SELECT 100 ,1 ,'John'
INSERT INTO #Table (ID,[Type],String) SELECT 100 ,2 ,'Milton'
INSERT INTO #Table (ID,[Type],String) SELECT 101 ,1 ,'Johny'
INSERT INTO #Table (ID,[Type],String) SELECT 101 ,2 ,'Gaddar'
SELECT IDs.ID,
tName.String NAME,
tSur.String Surname
FROM (
SELECT DISTINCT ID
FROM #Table
) IDs LEFT JOIN
#Table tName ON IDs.ID = tName.ID AND tName.[Type] = 1 LEFT JOIN
#Table tSur ON IDs.ID = tSur.ID AND tSur.[Type] = 2
OK, i do not think that you will find a solution to what you are looking for. From UPDATE (Transact-SQL) it states
Using UPDATE with the FROM Clause
The results of an UPDATE statement are
undefined if the statement includes a
FROM clause that is not specified in
such a way that only one value is
available for each column occurrence
that is updated, that is if the UPDATE
statement is not deterministic. For
example, in the UPDATE statement in
the following script, both rows in
Table1 meet the qualifications of the
FROM clause in the UPDATE statement;
but it is undefined which row from
Table1 is used to update the row in
Table2.
USE AdventureWorks;
GO
IF OBJECT_ID ('dbo.Table1', 'U') IS NOT NULL
DROP TABLE dbo.Table1;
GO
IF OBJECT_ID ('dbo.Table2', 'U') IS NOT NULL
DROP TABLE dbo.Table2;
GO
CREATE TABLE dbo.Table1
(ColA int NOT NULL, ColB decimal(10,3) NOT NULL);
GO
CREATE TABLE dbo.Table2
(ColA int PRIMARY KEY NOT NULL, ColB decimal(10,3) NOT NULL);
GO
INSERT INTO dbo.Table1 VALUES(1, 10.0), (1, 20.0), (1, 0.0);
GO
UPDATE dbo.Table2
SET dbo.Table2.ColB = dbo.Table2.ColB + dbo.Table1.ColB
FROM dbo.Table2
INNER JOIN dbo.Table1
ON (dbo.Table2.ColA = dbo.Table1.ColA);
GO
SELECT ColA, ColB
FROM dbo.Table2;
Astander is correct (I am accepting his answer). The update is not happening because of a read UNCOMMITTED issue but because of the multiple rows returned by the JOIN. I have verified this. UPDATE picks only the first row generated from the multiple records to update the original table. This is the behavior for MSSQL, Sybase and such RDMBMSs but Oracle does not allow this kind of an update an d it throws an error. I have verified this thing for MSSQL.
And again MSSQL does not support updating a cell with UNCOMMITTED data. Don't know the status with other RDBMSs. And I have no idea if anyRDBMS provides with in the query ISOLATION level management.
An alternate solution will be to do it in two steps, Aggregate to unpivot and then insert. This has lesser scans compared to methods given in above answers.
INSERT INTO Customers
SELECT
ID
,MAX(CASE WHEN Type = 1 THEN String ELSE NULL END) AS FirstName
,MAX(CASE WHEN Type = 2 THEN String ELSE NULL END) AS LastName
FROM Strings
GROUP BY ID
Thanks to my friend Roji Thomas for helping me with this.