Are multiple stored procedures necessary here? - sql

I've got a table with a structure like
create table DrugInteractions
(ndc_fk varchar(50) not null
,ndc_pk varchar(50) not null
,InteractionSeverity int not null,
primary key(ndc_fk,ndc_pk)
)
and another with a structure like'
create table DrugList
(char(11) not null
,drug_name varchar(50) not null
,drug_class char(3) not null,
primary key(ndc)
)
There is a 1-many relationship with drug_class and drug_name, but of course a 1-1 relationship between drug_name and drug_class. I currently have three stored procedures which add records to the DrugInteraction table with the following scenarios
All drugs of one class interact with another class
CREATE proc spInsertDrugInteractionsByDrugClass
#referenceClass char(3)
,#interactingClass char(3)
as
begin
;with x
as
(
select distinct
drug_name
from DrugList
where drug_class =#interactingClass
)
,y
as
(
select drug_name
from Druglist
where drug_class = #referenceClass
)
insert into DrugInteractions(ndc_pk,ndc_fk)
select distinct
upper(x.drug_name)
,upper(y.drug_name)
from y,x
end
Sample output from sproc
ndc_fk ncd_pk
drug1 drug2
drug1 drug3
drug1 drug4
Another situation is where there is one 'reference' drug class and other drug names entered as parameters to the sproc that that may or may not be the same drug_class
CREATE proc spInsertDrugInteractionsByClassAndName
#referenceClass char(3)
,#d1 varchar(50) = null
,#d2 varchar(50) = null
,#d3 varchar(50) = null
,#d4 varchar(50) = null
,#d5 varchar(50) = null
as begin
;with refClassDrugNames
as
(
select distinct upper(drug_name) as drug_name
from DrugList
where drug_class = #referenceClass
),
drugName
as
(
select distinct upper(drug_name) as drug_name
from DrugList
where drug_name in (#d1,#d2,#d3,#d4,#d5)
)
insert into DrugInteractions(ndc_pk,ndc_fk)
select * from refClassDrugNames,drugName
end
Sample output from this sproc would be:
ndc_pk ndc_fk
drug1 refDrug
drug2 refDrug
drug3 refDrug
drug4 refDrug
for situations such as this, is it good/bad design to have multiple stored procedures? I can't help but think there's got to be a way to make one sproc cover these two scenarios. Can something akin to this be done in SQL Server only?

Related

Replicating rows in a table by the columns

I need to develop a report I create it in excel but it became so heavy that even my PC cannot open it.
Right now I decide to create it with SQL.
The excel input is something like this:
Service_order PENDING_DAYS SERVICE_TYPE ASC code INOUTWTY Part_code1 Part_code2 Part_code3 Part_code4 Part_code5
4182864919 18 CI 3440690 LP GH82-11218A GH96-09406A GH81-13594A GH02-11552A GH02-11553A
4182868153 18 CI 4285812 LP GH97-17670B
4182929636 17 CI 4276987 LP GH97-17260C GH02-10203A
4182953067 16 CI 3440690 LP GH97-17940C
4182954688 16 CI 6195657 LP GH82-10555A GH97-17852A GH81-13071A
4182955036 16 PS 6195657 LP GH97-17940C
and the result using this codes
=HLOOKUP(Sheet3!A$1;Sheet3!$A$1:$F$10000;CEILING((ROW()-ROW(Sheet3!$A$1))/5+1;1);FALSE)"
=OFFSET(WholePart;TRUNC((ROW()-ROW($G$2))/COLUMNS(WholePart));MOD(ROW()-ROW($G$2);COLUMNS(WholePart));1;1)
* WholePart is partCode values.
are like this:
What I want to do is to convert those formula or have an output just like this.
Appreciate it.
My advice is prepare your data in EXCEL and load into normalized tables. Then you can get the result by joining the tables.
create table T1(
Service_order bigint primary key,
PENDING_DAYS int,
SERVICE_TYPE varchar(10),
ASC_code int,
INOUTWTY varchar(10)
);
create table T2(
Service_order bigint,
Part_code varchar(50)
);
insert into T1(Service_order, PENDING_DAYS, SERVICE_TYPE, ASC_code, INOUTWTY)
values
(4182864919 , 18,'CI',3440690,'LP'),
(4182868153 , 18,'CI',4285812,'LP'),
(4182929636 , 17,'CI',4276987,'LP'),
(4182953067 , 16,'CI',3440690,'LP'),
(4182954688 , 16,'CI',6195657,'LP'),
(4182955036 , 16,'PS',6195657,'LP');
insert into T2(Service_order, Part_code)
values
(4182864919,'GH82-11218A'),
(4182864919,'GH96-09406A'),
(4182864919,'GH81-13594A'),
(4182864919,'GH02-11552A'),
(4182864919,'GH02-11553A'),
(4182868153,'GH97-17670B'),
(4182929636,'GH97-17260C'),
(4182929636,'GH02-10203A'),
(4182953067,'GH97-17940C'),
(4182954688,'GH82-10555A'),
(4182954688,'GH97-17852A'),
(4182954688,'GH81-13071A'),
(4182955036,'GH97-17940C')
select T1.*, T2.Part_code
from T1
join T2 on T1.Service_order = T2.Service_order
order by T1.Service_order, T2.Part_code;
EDIT
Alternatively you can load original EXCEL data (first table) and them normalize it in SQL.
-- create normalized tables
create table T1(
Service_order bigint primary key,
PENDING_DAYS int,
SERVICE_TYPE varchar(10),
ASC_code int,
INOUTWTY varchar(10)
);
create table T2(
Service_order bigint,
Part_code varchar(50)
);
-- load data from excel.
create table excelData(
Service_order bigint,
PENDING_DAYS int,
SERVICE_TYPE varchar(10),
ASC_code int,
INOUTWTY varchar(10),
Part_code1 varchar(50),
Part_code2 varchar(50),
Part_code3 varchar(50),
Part_code4 varchar(50),
Part_code5 varchar(50)
);
-- Below i use sample data insert instead of load.
insert into excelData(Service_order, PENDING_DAYS, SERVICE_TYPE, ASC_code, INOUTWTY
,Part_code1, Part_code2, Part_code3, Part_code4, Part_code5)
values
(4182864919 , 18,'CI',3440690,'LP','GH82-11218A','GH96-09406A','GH81-13594A','GH02-11552A','GH02-11553A'),
(4182868153 , 18,'CI',4285812,'LP','GH97-17670B','','','',''),
(4182929636 , 17,'CI',4276987,'LP','GH97-17260C','GH02-10203A','','',''),
(4182953067 , 16,'CI',3440690,'LP','GH97-17940C','','','',''),
(4182954688 , 16,'CI',6195657,'LP','GH82-10555A','GH97-17852A','GH81-13071A','',''),
(4182955036 , 16,'PS',6195657,'LP','GH97-17940C','','','','');
-- store loaded data into normalized tables.
insert into T1(Service_order, PENDING_DAYS, SERVICE_TYPE, ASC_code, INOUTWTY)
select Service_order, PENDING_DAYS, SERVICE_TYPE, ASC_code, INOUTWTY
from excelData;
insert into T2(Service_order, Part_code)
select Service_order, Part_code
from excelData
cross apply (
--unpivot
select Part_code1 as Part_code where len(Part_code1) > 0
union all
select Part_code2 where len(Part_code2) > 0
union all
select Part_code3 where len(Part_code3) > 0
union all
select Part_code4 where len(Part_code4) > 0
union all
select Part_code5 where len(Part_code5) > 0
) unp;
-- check it
select * from T1;
select * from T2;

Creating and populating a table in T-SQL

I'm new to T-SQL and trying to learn how to create a script in T-SQL to create and populate a table(StaffData).The StaffData is defined as below:
staffid – integer primary key, identity starting at 1, increments by 1
managerid – int, allows nulls, pointer to another record in managertable
name – string of 50 characters
salary – money
What can I do to generate table and fill it with set of data..?
Here's the correct SQL. I've tested it (just spotted that you want managerId nullable - I've added this):
it uses better conventions for your table and column names (you shouldn't be using 'data' in table names - we know it contains data)
it names your primary key constraints, which is better practice - you can do something similar for the FK constraint if you want, I've just done it inline
it uses 'USE' and 'GO' statements to ensure you're creating things on the right database (critical when you're working on big production systems).
it uses nvarchar columns - you need these to reliably store data from international character sets (e.g the manager has a Russian name)
I'm using nvarchar(max) as you can't be sure that a name will only be 50 characters. Use nvarchar(50) if you must, but database space isn't usually a big deal.
You need to create the Manager table first, as your Staff table depends on it:
USE [yourDatabaseName] -- you don't need the square brackets, but they don't hurt
-- Create ManagerTable
CREATE TABLE Manager
(
id int IDENTITY(1,1),
name nvarchar(max),
CONSTRAINT pk_manager PRIMARY KEY (id)
)
CREATE TABLE Staff
(
id int IDENTITY(1,1),
name nvarchar(max),
salary money,
managerId int FOREIGN KEY REFERENCES Manager(id) NULL,
CONSTRAINT pk_staff PRIMARY KEY (id)
)
--To populate Manager table:
INSERT INTO [Manager]
(
-- id column value is auto-generated
name
)
VALUES
(
'John Doe'
)
--To populate Staff table:
INSERT INTO [Staff]
(
-- id column value is auto-generated
name, salary, managerId
)
VALUES
(
'Jane Doe', 60000, 1
)
GO
To create the two database tables:
-- Create StaffData
CREATE TABLE StaffData
(
staffid int PRIMARY KEY IDENTITY,
managerid int
)
-- Create ManagerTable
CREATE TABLE ManagerTable
(
managerid int,
name varchar(50),
salary money
)
To populate StaffData table:
INSERT INTO [StaffData]
(
--staffid - this column value is auto-generated
[managerid]
)
VALUES
(
-- staffid - int
12345 -- managerid - int
)
To populate ManagerTable table:
INSERT INTO [ManagerTable]
(
[managerid],
[name],
[salary]
)
VALUES
(
12345, -- managerid - int
'Juan Dela Cruz', -- name - varchar
15000 -- salary - money
)
To select the data if i understand you in your word Pointer here is the query using INNER JOIN joining the two tables using their managerid
SELECT *
FROM [StaffData]
INNER JOIN [ManagerTable]
ON [StaffData].managerid = [ManagerTable].managerid

Insert - Select keeping identity mapping

I have 2 tables, and im trying to insert data from one to another and keepeng the mappings between ids.
I found here someone with the same problem, but the solution isnt good for me.
here is the example:
the two tables
CREATE TABLE [source] (i INT identity PRIMARY KEY, some_value VARCHAR(30))
CREATE TABLE [destination] (i INT identity PRIMARY KEY, some_value VARCHAR(30))
CREATE TABLE [mapping] (i_old INT, i_new INT) -- i_old is source.i value, i_new is the inserted destination.i column
some sample data
INSERT INTO [source] (some_value)
SELECT TOP 30 name
FROM sysobjects
INSERT INTO [destination] (some_value)
SELECT TOP 30 name
FROM sysobjects
Here, i want to transfer everything from source into destination, but be able to keep a mapping on the two tables:
I try to use OUTPUT clause, but i cannot refer to columns outside of the ones being inserted:
INSERT INTO [destination] (some_value)
--OUTPUT inserted.i, s.i INTO [mapping] (i_new, i_old) --s.i doesn't work
SELECT some_value
FROM [source] s
Anyone has a solution for this?
Not sure is it write way but it works :D
MERGE [#destination] AS D
USING [#source] AS s
ON s.i <> s.i
WHEN NOT MATCHED BY TARGET
THEN
INSERT (some_value) VALUES (some_value)
OUTPUT inserted.i, s.i INTO [#mapping] (i_new, i_old);
try this sql below if you don't have permission to modify the tables:
The idea is using a temp table to be a bridge between destination table and the mapping table.
SQL Query:
declare #source table (i INT identity PRIMARY KEY, some_value VARCHAR(30))
declare #destination table (i INT identity PRIMARY KEY, some_value VARCHAR(30))
declare #mapping table (i_old INT, i_new INT) -- i_old is source.i value, i_new is the inserted destination.i column
declare #tempSource table
(
id_source INT identity , source_value VARCHAR(30)
,Id_New int,source_new VARCHAR(30)
)
insert into #source
output inserted.i, inserted.some_value into #tempSource(id_source,source_value)
SELECT TOP 10 name
FROM sysobjects
--select * from #tempsource
insert into #destination
OUTPUT inserted.i, inserted.some_value INTO #tempSource (Id_New,source_new)
select source_value from #tempSource
insert into #mapping
select Id_source, Id_New from
(
select a.id_source, a.source_value
from
#tempSource a
where id_source is not null and source_value is not null
) aa
inner join
(
select a.Id_New, a.source_new
from
#tempSource a
where Id_New is not null and source_new is not null
) bb on aa.source_value = bb.source_new
select * from #mapping
The mapping table result:
i_old i_new
----------- -----------
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10

Insert a row in Order table and multiple rows in OrderDetails table via stored procedure

I have five tables named guest, orders, order_details, food, and employees. Their characteristics are as follows:
create table guest
(
guest_id int primary key identity(1,1),
guest_fname nvarchar(50),
national_id int,
mobile nvarchar(50),
nationality_id int
constraint c15
foreign key(nationality_id) references nationality(nationality_id)
)
create table employees
(
emp_id int primary key ,
emp_fname nvarchar(50),
constraint c1
--foreign key(mgr_id) references employees(emp_id),
foreign key(super_id) references employees(emp_id),
)
go
create table food
(
food_id int primary key identity(1,1),
food_name nvarchar(50),
food_price money,
food_desc nvarchar(200),
cat_id int
constraint c5
foreign key(cat_id) references food_categories(cat_id)
)
go
create table orders
(
order_id int primary key identity(1,1),
order_date datetime,
total float,
guest_id int,
emp_id int,
is_paid bit
constraint c6
foreign key(guest_id) references guest(guest_id),
foreign key(emp_id) references employees(emp_id)
)
go
create table order_details
(
order_id int,
food_id int,
price money,
qty int
constraint c7
primary key(order_id,food_id),
foreign key(order_id) references orders(order_id),
foreign key(food_id) references food(food_id)
)
Go
There is a 1-to-M relationship between orders and order_details.
I want to insert a single row into orders and multiple rows into order_details via a stored procedure. Please help me!
Please give me a stored procedure and explain its algorithm to point me in the right direction.
As #thepirat000 points out, you can use a TVP for the order details/line items since you are using SQL Server 2012. Pinal Dave has a great article on TVPs that you may find helpful. Combine the TVP with scalar parameters for order properties.
For example:
-- NOTE: *Not* accounting for order_id in this case - i.e. a new order where
-- order_id is an identity value.
CREATE TYPE [dbo].[order_details_type] AS TABLE
(
food_id int,
price money,
qty int
)
GO
CREATE PROCEDURE [dbo].[usp_insert_order]
(
#order_id int output,
#order_date datetime = NULL,
#total float,
#guest_id int,
#emp_id int,
#is_paid bit = 0,
#details order_details_type readonly
)
AS
BEGIN
SET NOCOUNT ON;
-- TODO: Wrap the inserts into dbo.orders and dbo.order_details in a
-- transaction as desired.
-- TODO: Check that #order_id does not already exist in dbo.orders and
-- dbo.order_details etc.
-- FORNOW: Proceed optimistically. :}
IF #order_date IS NULL
SET #order_date = GETDATE();
INSERT INTO dbo.orders (order_date, total, guest_id, emp_id, is_paid)
VALUES (#order_date, #total, #guest_id, #emp_id, #is_paid);
SET #order_id = SCOPE_IDENTITY();
INSERT INTO dbo.order_details (order_id, food_id, price, qty)
SELECT #order_id, food_id, price, qty
FROM #details;
END
GO
Executing such a stored procedure is pretty straighforward, as you prepare the TVP argument much like you would a table variable:
/*
* Order up!
*/
/* data I added for a quick check
SELECT TOP 1 * FROM dbo.employees;
--emp_id emp_fname
--1 Wendy
SELECT TOP 1 * FROM dbo.guest;
--guest_id guest_fname national_id mobile
--1 Joe 1 619-555-1212
SELECT TOP 2 * FROM dbo.food;
--food_id food_name food_price food_desc
--1 Onion Rings 5.00 Beer-battered onion rings.
--2 Kobe Burger 10.00 Kobe-beef burger.
*/
-- Prepare the order.
DECLARE #order_id int; -- to get on order insert
DECLARE #now datetime = getdate();
DECLARE #order_details order_details_type;
INSERT INTO #order_details
SELECT 1, 5, 1
UNION
SELECT 2, 10, 2;
-- Insert the order.
EXEC dbo.usp_insert_order
#order_id = #order_id OUTPUT,
#order_date=#now,
#total=25,
#guest_id=1,
#emp_id=1,
#is_paid=0,
#details=#order_details;
/*
* Check the order.
*/
SELECT * FROM dbo.orders WHERE order_id = #order_id;
--order_id order_date total guest_id emp_id is_paid
--1 2014-03-13 21:44:45.400 25 1 1 0
SELECT * FROM dbo.order_details WHERE order_id = #order_id;
--order_id food_id price qty
--1 1 5.00 1
--1 2 10.00 2
Another option that pre-dates TVPs may interest you too - using an XML parameter for the order details/line items and scalar parameters for order properties. Related resources and examples abound.
A variation of the XML-param approach that is to pass XML for the order and its details/line items; but I think this is overkill personally.
you need 2 store procs, 1 for order and 1 for Order_details, in the last one add update statement for food table qty. you do not want to make 1 sproc for all

insert data into several tables

Let us say I have a table (everything is very much simplified):
create table OriginalData (
ItemName NVARCHAR(255) not null
)
And I would like to insert its data (set based!) into two tables which model inheritance
create table Statements (
Id int IDENTITY NOT NULL,
ProposalDateTime DATETIME null
)
create table Items (
StatementFk INT not null,
ItemName NVARCHAR(255) null,
primary key (StatementFk)
)
Statements is the parent table and Items is the child table. I have no problem doing this with one row which involves the use of IDENT_CURRENT but I have no idea how to do this set based (i.e. enter several rows into both tables).
Thanks.
Best wishes,
Christian
Another possible method that would prevent the use of cursors, which is generally not a best practice for SQL, is listed below... It uses the OUTPUT clause to capture the insert results from the one table to be used in the insert to the second table.
Note this example makes one assumption in the fact that I moved your IDENTITY column to the Items table. I believe that would be acceptable, atleast based on your original table layout, since the primary key of that table is the StatementFK column.
Note this example code was tested via SQL 2005...
IF OBJECT_ID('tempdb..#OriginalData') IS NOT NULL
DROP TABLE #OriginalData
IF OBJECT_ID('tempdb..#Statements') IS NOT NULL
DROP TABLE #Statements
IF OBJECT_ID('tempdb..#Items') IS NOT NULL
DROP TABLE #Items
create table #OriginalData
( ItemName NVARCHAR(255) not null )
create table #Statements
( Id int NOT NULL,
ProposalDateTime DATETIME null )
create table #Items
( StatementFk INT IDENTITY not null,
ItemName NVARCHAR(255) null,
primary key (StatementFk) )
INSERT INTO #OriginalData
( ItemName )
SELECT 'Shirt'
UNION ALL SELECT 'Pants'
UNION ALL SELECT 'Socks'
UNION ALL SELECT 'Shoes'
UNION ALL SELECT 'Hat'
DECLARE #myTableVar table
( StatementFk int,
ItemName nvarchar(255) )
INSERT INTO #Items
( ItemName )
OUTPUT INSERTED.StatementFk, INSERTED.ItemName
INTO #myTableVar
SELECT ItemName
FROM #OriginalData
INSERT INTO #Statements
( ID, ProposalDateTime )
SELECT
StatementFK, getdate()
FROM #myTableVar
You will need to write an ETL process to do this. You may want to look into SSIS.
This also can be done with t-sql and possibly temp tables. You may need to store unique key from OriginalTable in Statements table and then when you are inserting Items - join OriginalTable with Statements on that unique key to get the ID.
I don't think you could do it in one chunk but you could certainly do it with a cursor loop
DECLARE #bla char(10)
DECLARE #ID int
DECLARE c1 CURSOR
FOR
SELECT bla
FROM OriginalData
OPEN c1
FETCH NEXT FROM c1
INTO #bla
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO Statements(ProposalDateTime) VALUES('SomeDate')
SET #ID = SCOPE_IDENTITY()
INSERT INTO Items(StateMentFK,ItemNAme) VALUES(#ID,#bla)
FETCH NEXT FROM c1
INTO #bla
END
CLOSE c1
DEALLOCATE c1