Liquibase where clause with sub query - liquibase

Is it possible to write sub query in liquibase without using the <sql> tag
Lets say we have the following tables:
Table Cars:
CarID int
CarName nvarchar(100)
Table Drivers:
DriverID int
DriverName nvarchar(100)
Table CarDrivers:
CarID int
DriverID int
Example
<delete tableName="CarDrivers">
<where>DriverID in select driver.DriverID from Drivers driver where driver.DriverName = 'John Doe'</where>
</delete>
Is it possible to compute such subquery as select driver.DriverID from Drivers driver where driver.DriverName = 'John Doe' inside a liquibase <where> tag?

I think you can try like this:
<delete tableName="CarDrivers">
<where>driverId IN (SELECT driverId FROM Drivers WHERE driverName IN ('John', 'Jack'))</where>
</delete>

Related

Stored Procedure: Keep Query Result (as Table) in variable

I'm from Entity Framework background and I don't do much pure SQL development.
However, when dealing with EF we can do:
var persons = from x in db.Person
where x.Name.Equals("Something")
select x;
or
IQuerable<Person> persons = from x in db.Person
where x.Name.Equals("Something")
select x;
But when dealing with stored procedures, how could this be done exactly, like for example I can capture a variable by declaring DECLARE #Name NVARCHAR(15), but how to get the returned table result?
Create a table variable and insert your data into that:
DECLARE #Person table(
FirstName nvarchar(15),
LastName nvarchar(15)
);
Insert Into #Person(FirstName, LastName)
Select Firstname, LastName
From Person
Where LastName = #LastName; -- where #lastname comes from a sp parameter.
Now do stuff like
Select count(*) from #Person;
simply declare a table variable:
DECLARE #TableVariable TABLE
(
PrimaryKey INT,
Name NVARCHAR(MAX)
)
and insert the results into this table.
One way as answered by #ps2goat. And if you want Stored Procedure output into table then you can do like this :-
DECLARE #Person table
(
Id Int Identity(1,1)
,FirstName nvarchar(15)
,LastName nvarchar(15)
);
Insert Into #Person(FirstName,LastName)
Exec dbo.USP_SomeStoredProcedure <sp's parameters>;
Select Top 1
FirstName
,LastName
From #Person As p
Order By p.Id Asc
Note:- your calling procedure output and table column exactly match should be same
Well I found the right answer myself. I think I have to use cursors and that is the best to do so.

SSIS populating cross reference table

Destination tables look like this:
Source table look like this:
Customer
CustomerId FirstName LastName Email Address1 Address2 City Zip
Person table in destination is a base table (which will later be inherited by new customer table). So I am trying to export a row from one table and populate 3 tables in destination.
I managed to do this in following way:
Get records from source table (Customer)
Create empty AddressId field
Populate Address table using OLE DB Command task (it calls stored procedure which returns SCOPE_IDENTITY() that's mapped to AddressId field)
Repeat step 3 for populating Person table (and retrieving PersonId
Populate cross reference table PersonAddress using PersonId and AddressId fields
Screenshot of this package is below.
Biggest issue with this approach is that OLE DB Command task is inserting row by row and it makes the whole package extremely slow. Is it possible to achieve the same thing but using fast load?
I am able to do it using OLE DB Command task which calls the stored procedure and then
I don't think you need SSIS.
You can use OUTPUT clause of INSERT which returns all identity keys to a temporary table
Lets try reproduce your scenario...
set nocount on
go
create table Customer (CustomerId int, CustomerName varchar(100) null, Address1 varchar(100) null, Address2 varchar(100) )
create table [Person] (PersonId int identity, PersonName varchar(100) null)
create table [Address] (AddressId int identity, AddressLine varchar(100) null)
create table [PersonAddress] (AddressId int, PersonId int )
go
-- create some data...
insert into Customer (CustomerId) values ( 1000000 + convert(int, RAND() * 1000000) )
go 1000
update Customer
set CustomerName = 'CustomerName ' + convert(varchar, CustomerId),
Address1 = 'Address1 ' + convert(varchar, CustomerId),
Address2 = 'Address2 ' + convert(varchar, CustomerId)
go
declare #identities_Person table ([rownumber] int identity, id int)
declare #identities_Address table ([rownumber] int identity, id int)
insert into Person (PersonName)
output inserted.PersonId into #identities_Person
select
c.CustomerName
from Customer c
order by c.CustomerId
insert into [Address] (AddressLine)
output inserted.AddressId into #identities_Address
select
c.Address1
from Customer c
order by c.CustomerId
insert into [PersonAddress] (PersonId, AddressId)
select p.id, a.id
from #identities_Address a
inner join #identities_Person p on p.rownumber = a.rownumber
select *
from PersonAddress pa
inner join [Address] a on a.AddressId = pa.AddressId
inner join [Person] p on p.PersonId = pa.PersonId

Insert New data into multiple tables with Foreign Key simultaneously in One query in sql server

I have two tables:
tblDepartment:
Id Name
and
tblEmployee:
Id FullName Dept_ID
Dept_ID is a foreign key for tblDepartment
I want to insert a new record into both tables.
I tried this:
declare #id int
insert dbo.tblDepartment
select Name='Name1'
select id = scope_Identity()
insert dbo.tblEmployee
select FullName = 'Name1'
select Dept_Id=#id
select Id=#id
However, this is not working. I searched through other posts, but they contain solutions for inserting existing data from one table into another, not creating a new record. How can I do this in one query?
You need to use variables properly along with column lists for inserts. Assuming you are using SQL Server:
declare #id int ;
insert dbo.tblDepartment(Name)
select 'Name1';
select #id = scope_Identity();
insert dbo.tblEmployee(FullName, Dept_Id)
select 'Name1', #id;
Also, scope_identity() is okay for learning about such id's. The safe way to really get this information is to use the output clause.
declare #id int
insert dbo.tblDepartment(Name)
select 'Name1'
-- Don't insert any other statements before next line...
select #id=scope_Identity()
insert dbo.tblEmployee(Fullname, Dept_ID)
select 'Name1', #id

Simplest way to insert a related row for each row in a table and link them together?

What is the simplest way to insert a Car for each user in Users and set the users CarID to the ID of the inserted Car?
[Users]
- ID
- Name
- CarID
[Cars]
- ID (Auto increment)
- Name
Sorry if this might be a duplicate question but I can't find any simple solutions. Everything I've found is using complicated cursors, pointers etc.
A simple and reusable syntax for this would save me hours while migrating data during system upgrades etc.
If you are on SQL Server 2008 or later you can use merge and output something like this.
Sample tables and data:
declare #Users table
(
ID int identity primary key,
Name varchar(10) not null,
CarID int null
);
declare #Cars table
(
ID int identity primary key,
Name varchar(10) not null
);
insert into #Users(Name) values ('User1'),('User2'),('User3');
Add one care for each user and move the auto-generated CarID back to Users.
declare #ID table(CarID int, UserID int)
merge #Cars as C
using #Users as U
on 0 = 1
when not matched then
insert (Name) values ('CarName')
output inserted.ID, U.ID into #ID;
update U
set CarID = I.CarID
from #Users as U
inner join #ID as I
on U.ID = I.UserID
Try it out on SE Data.
More info on the merge/output trick can be found here.
I'm assuming the code you're using lets you call stored procedures?
create procedure dbo.CarUserInsert_sp
(
#p_UserID int,
#p_UserName varchar(100),
#p_CarID int,
#p_CarName varchar(100)
)
as
if not exists ( select 1 from Cars where ID = #p_CarID )
insert Cars values ( #p_CarID, #p_CarName )
if exists (select 1 from Users where ID = #p_UserID )
update Users set Name = #p_UserName, CarID = #p_CarID where ID = #p_UserID
else
insert Users values ( #p_UserID, #p_UserName, #p_CarID )
go
try this:
insert into cars (name)
select distinct(name) from users
update user
set carId = (select ID from cars where cars.name=user.Name)

versioning of a table

anybody has seen any examples of a table with multiple versions for each record
something like if you would had the table
Person(Id, FirstName, LastName)
and you change a record's LastName than you would have both versions of LastName (first one, and the one after the change)
I've seen this done two ways. The first is in the table itself by adding an EffectiveDate and CancelDate (or somesuch). To get the current for a given record, you'd do something like: SELECT Id, FirstName, LastName FROM Table WHERE CancelDate IS NULL
The other is to have a global history table (which holds all of your historical data). The structure for such a table normally looks something like
Id bigint not null,
TableName nvarchar(50),
ColumnName nvarchar(50),
PKColumnName nvarchar(50),
PKValue bigint, //or whatever datatype
OriginalValue nvarchar(max),
NewValue nvarchar(max),
ChangeDate datetime
Then you set a trigger on your tables (or, alternatively, add a policy that all of your Updates/Inserts will also insert into your HX table) so that the correct data is logged.
The way we're doing it (might not be the best way) is to have an active bit field, and a foreign key back to the parent record. So for general queries you would filter on active employees, but you can get the history of a single employee with their Employee ID.
declare #employees
(
PK_emID int identity(1,1),
EmployeeID int,
FirstName varchar(50),
LastName varchar(50),
Active bit,
FK_EmployeeID int
primary key(PK_emID)
)
insert into #employees
(
EmployeeID,
FirstName,
LastName,
Active,
FK_EployeeID
)
select 1, 'David', 'Engle', 1,null
union all
select 2, 'Amy', 'Edge', 0,null
union all
select 2, 'Amy','Engle',1,2