Replicating rows in a table by the columns - sql

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;

Related

is there way to apply row updates without looping

i would like to pay for different invoices using different credits i have.
drop table #InvoicesWithBalances
drop table #AvailableCredits
create table #InvoicesWithBalances
(
InvoiceKey decimal(18,0) not null,
APBalance decimal(18,6) null,
BalanceAfterCreditApplied decimal(18,6) null,
)
create table #AvailableCredits
(
credit_id int identity(1,1),
StartingBalance decimal(18,6) null,
CurrentBalance decimal(18,6) null,
)
insert into #InvoicesWithBalances values (5452, 13744.080000, 13744.080000)
insert into #InvoicesWithBalances values (7056, 13744.080000, 13744.080000)
insert into #InvoicesWithBalances values (7438, 500.000000, 500.000000 )
insert into #AvailableCredits values ( -13744.080000, -13744.080000)
insert into #AvailableCredits values ( -13700.080000, -13700.080000)
insert into #AvailableCredits values ( -500.000000, -500.000000)
insert into #AvailableCredits values ( -500.000000, -500.000000)
select * from #InvoicesWithBalances
select * from #AvailableCredits
If I was doing a looping solution I would take the largest credit and start applying it to the invoices in order of largest to smallest until the balance of the credit is zero, then I would move on to the next to the next credit until I had no credits and no invoices left.
In the example below the first 2 credits should be fully used. The third credit should be partially used and the last credit should go untouched
Any advice?
I have tried to simulate your example here:
create table InvoicesWithBalances
(
InvoiceKey int not null,
APBalance int null,
BalanceAfterCreditApplied int null,
);
create table AvailableCredits
(
credit_id int identity(1,1),
StartingBalance int null,
CurrentBalance int null,
);
insert into InvoicesWithBalances values (5452, 13744, 13744);
insert into InvoicesWithBalances values (7056, 13744, 13744);
insert into InvoicesWithBalances values (7438, 500, 500);
insert into AvailableCredits values ( -13744, -13744);
insert into AvailableCredits values ( -13700, -13700);
insert into AvailableCredits values ( -500, -500);
insert into AvailableCredits values ( -500, -500);
create table #invoice (invoice_row_num int, InvoiceKey int, APBalance int, BalanceAfterCreditApplied int);
insert into #invoice
select ROW_NUMBER() OVER (ORDER BY APBalance desc) as row_num, InvoiceKey, APBalance, BalanceAfterCreditApplied FROM InvoicesWithBalances;
create table #credits (credit_row_num int, StartingBalance int, CurrentBalance int);
insert into #credits
select ROW_NUMBER() OVER (ORDER BY StartingBalance asc) as row_num, StartingBalance, CurrentBalance FROM AvailableCredits;
create table #invoice_credit_list (invoice_credit_row_num int, init_invoice int, init_credit int);
if ((select max(invoice_row_num) from #invoice) > (select max(credit_row_num) from #credits))
insert into #invoice_credit_list
select i.invoice_row_num , i.APBalance, (-isnull(c.StartingBalance,0)) from
#invoice i
left join
#credits c
on
i.invoice_row_num = c.credit_row_num;
else
insert into #invoice_credit_list
select c.credit_row_num, isnull(i.APBalance,0), (-c.StartingBalance) from
#credits c
left join
#invoice i
on
i.invoice_row_num = c.credit_row_num;
with cte as
(
select
invoice_credit_row_num,
init_invoice,
init_credit,
case when init_invoice >= init_credit then
init_invoice - init_credit
else
0
end as 'invoice_remaining',
case when init_credit >= init_invoice then
init_credit - init_invoice
else
0
end as 'credit_remaining'
from
#invoice_credit_list i
where
i.invoice_credit_row_num = 1
UNION ALL
select
i.invoice_credit_row_num,
i.init_invoice + cte.invoice_remaining as 'init_invoice',
i.init_credit + cte.credit_remaining as 'init credit',
case when (i.init_invoice + cte.invoice_remaining) >= (i.init_credit + cte.credit_remaining ) then
(i.init_invoice + cte.invoice_remaining) - (i.init_credit + cte.credit_remaining )
else
0
end as 'invoice_remaining',
case when (i.init_credit + cte.credit_remaining) >= (i.init_invoice + cte.invoice_remaining) then
(i.init_credit + cte.credit_remaining) - (i.init_invoice + cte.invoice_remaining)
else
0
end as 'credit_remaining'
from
#invoice_credit_list i
inner join
cte
ON
i.invoice_credit_row_num - 1 = cte.invoice_credit_row_num
AND
i.invoice_credit_row_num > 1
)
select * from cte;
and you can also find this simulation with output here: https://rextester.com/SJYGV76640
The 'cte' table in the simulation will give you all the details.
Eventhough, this is now done in db side, I am not sure of its performance. So, please compare and evaluate its performance.
Note:
if this is performing faster, well and good. But, at most, don't opt for loops in T-SQL.
If this is not performing better, go for loops in any other programming language like C#, VB,.. if that is possible.
If no other option works for you, go for loops in T-SQL. But, with increasing data, I am not sure how the server will react :(
Hope this helps you :)

Creating Oracle procedure returning multiple records with two arguments

Code1 below create three tables and code2 show 2 rows.
Usually, how to make the code2 as a procedure which input two varchar (i.e.'20180101' , 20180106' at the last line of the code2), and ouput multiple records?
code1
CREATE TABLE equip_type (
EQUIP_TYPE VARCHAR(15),
EQUIP VARCHAR(15)
);
INSERT INTO equip_type VALUES ('A','e1');
CREATE TABLE output_history (
EQUIP VARCHAR(15),
MODEL VARCHAR(15),
Data1 VARCHAR(15),
QUANTITY NUMBER(10)
);
INSERT INTO output_history VALUES ('e1','m1','20180103',10);
INSERT INTO output_history VALUES ('e1','m1','20180106',20);
INSERT INTO output_history VALUES ('e1','m1','20180107',20);
CREATE TABLE time_history (
EQUIP VARCHAR(15),
MODEL VARCHAR(15),
Data2 VARCHAR(15),
time NUMBER(10)
);
INSERT INTO time_history VALUES ('e1','m1','20180101',6);
INSERT INTO time_history VALUES ('e1','m1','20180105',5);
INSERT INTO time_history VALUES ('e1','m1','20180107',5);
code2
SELECT
o.equip,
o.model,
o.data1,
o.quantity,
t.data2,
t.time,
e.equip_type
FROM
output_history o
INNER JOIN equip_type e ON e.equip = o.equip
INNER JOIN time_history t ON t.equip = o.equip AND t.data2 <= o.data1
WHERE NOT EXISTS (
SELECT 1
FROM time_history
WHERE
equip = o.equip
AND data2 <= o.data1
AND data2 > t.data2
) AND o.data1 >= '20180101' AND o.data1 <= '20180106';
You can do this by creating a type for the rowtype you want, another type for a nested table of the first type, and a function to return that table type.
But before you do that please, please, please rework your tables so you are not storing DATEs as VARCHAR2s.
create type mytype as object (
equip VARCHAR(15),
model VARCHAR(15),
data1 VARCHAR(15),
quantity NUMBER(10),
data2 VARCHAR(15),
time NUMBER(10),
equip_type VARCHAR(15)
);
/
create type mytype_tt as table of mytype;
/
create or replace function myfun( p1 varchar2, p2 varchar2 ) return mytype_tt
as
l_returnval mytype_tt;
begin
SELECT mytype(
o.equip,
o.model,
o.data1,
o.quantity,
t.data2,
t.time,
e.equip_type )
BULK COLLECT INTO l_returnval
FROM
output_history o
INNER JOIN equip_type e ON e.equip = o.equip
INNER JOIN time_history t ON t.equip = o.equip AND t.data2 <= o.data1
WHERE NOT EXISTS (
SELECT 1
FROM time_history
WHERE
equip = o.equip
AND data2 <= o.data1
AND data2 > t.data2
) AND o.data1 >= p1 AND o.data1 <= p2;
return l_returnval;
end;
/

Insert into select Subquery returned more than 1 value

I have the following code and it give me an error when the table #ListaDeProducto has more than 1 row. Any idea?
insert into Solicitud_Plastico_Interna_Detalle(
IDSolicitud_Plastico_Interna
,IDTipo_Producto
,Cantidad_Solicitada
,Create_User
,Create_Date
,Contingencia
,Total
)
select
#IdSolicitud
,IDTipo_Producto
,Cantidad_Requerida
,#USUARIO
,getdate()
,Contingencia
,Total
from #ListaDeProducto
Table schema
CREATE TYPE [ListaProductoTableType2] AS TABLE
(
IDTipo_Producto int,
Tipo_Producto varchar(1000),
Cantidad_Requerida int,
Contingencia int ,
Total int,
IdSolicitud_batch varchar(100)
)
GO
I still will bet there is some trigger in the table.
So why you dont try create a new table to prove this query is ok with multiple rows
CREATE TABLE Solicitud_Plastico_Temporal AS (
select
#IdSolicitud as IDSolicitud_Plastico_Interna
,IDTipo_Producto
,Cantidad_Requerida
,#USUARIO as Create_User
,getdate() as Create_Date
,Contingencia
,Total
from #ListaDeProducto
)

SQL join with previous row from the same group

I have a problem with sql query. I'm trying to create one time script which will put data to table. I have temporary table of new values ordered by date and i'm going to search previous value which have to have the same GroupId, TransactionId and FieldTypeId but ClaimModificationId have to be smaller.
Below i write a script which would be good if not error throwing on line:
where m2.ClaimModificationId < m1.ClaimModificationId
sql does not allow do referer to m1 table. Is there a method to write that condition differently?
create table #modifications (
[ClaimModificationId] INT IDENTITY(1,1),
[GroupId] INT,
[FieldTypeId] INT,
[FieldName] NVARCHAR(255),
[TransactionId] INT,
[NewValue] NVARCHAR(255),
[UserEmail] NVARCHAR(255),
[ModificationDate] DATETIME,
[Action] NVARCHAR(50))
select top 10
m1.[GroupId],
m1.[FieldTypeId],
m1.[FieldName],
m1.[TransactionId],
cm4.[NewValue] as OldValue,
m1.[NewValue],
m1.[UserEmail],
m1.[ModificationDate],
m1.[Action]
from #modifications m1
left join (
select max(m2.ClaimModificationId) as ClaimModificationId, m2.[GroupId], m2.[FieldTypeId], m2.TransactionId
from #modifications m2
where m2.ClaimModificationId < m1.ClaimModificationId
group by m2.GroupId, m2.FieldTypeId, m2.TransactionId) m3
on m3.groupId = m1.GroupId and m3.FieldTypeId = m1.FieldTypeId and m3.TransactionId = m1.TransactionId
LEFT JOIN #modifications cm4 ON m3.ClaimModificationId = cm4.ClaimModificationId
Try with OUTER APPLY:
SELECT TOP 10
m1.[GroupId] ,
m1.[FieldTypeId] ,
m1.[FieldName] ,
m1.[TransactionId] ,
cm4.[NewValue] AS OldValue ,
m1.[NewValue] ,
m1.[UserEmail] ,
m1.[ModificationDate] ,
m1.[Action]
FROM #modifications m1
OUTER APPLY ( SELECT MAX(m2.ClaimModificationId) AS ClaimModificationId ,
m2.[GroupId] ,
m2.[FieldTypeId] ,
m2.TransactionId
FROM #modifications m2
WHERE m2.ClaimModificationId < m1.ClaimModificationId
AND m2.groupId = m1.GroupId
AND m2.FieldTypeId = m1.FieldTypeId
AND m2.TransactionId = m1.TransactionId
GROUP BY m2.GroupId ,
m2.FieldTypeId ,
m2.TransactionId
) m3
LEFT JOIN #modifications cm4 ON m3.ClaimModificationId = cm4.ClaimModificationId
you can try something like this
create table #modifications
(
[ClaimModificationId] INT IDENTITY(1,1),
[GroupId] INT,
[FieldTypeId] INT,
[FieldName] NVARCHAR(255),
[TransactionId] INT,
[NewValue] NVARCHAR(255),
[UserEmail] NVARCHAR(255),
[ModificationDate] DATETIME,
[Action] NVARCHAR(50)
)
INSERT INTO #modifications values(1,1,'field',2,'new val1','email#email.com',GETDATE(),'inserted')
INSERT INTO #modifications values(1,2,'field',2,'val1','email#email.com',GETDATE(),'inserted')
INSERT INTO #modifications values(2,1,'field',3,'val2','email#email.com',GETDATE(),'inserted')
INSERT INTO #modifications values(1,1,'field',2,'val3','email#email.com',GETDATE(),'inserted')
INSERT INTO #modifications values(1,1,'field',2,'val4','email#email.com',GETDATE(),'inserted')
INSERT INTO #modifications values(1,1,'field',2,'val5','email#email.com',GETDATE(),'inserted')
INSERT INTO #modifications values(2,1,'field',3,'val5','email#email.com',GETDATE(),'inserted')
SELECT TOP 10
m1.[GroupId],
m1.[FieldTypeId],
m1.[FieldName],
m1.[TransactionId],
LAG([NewValue]) OVER(PARTITION by GroupId, FieldTypeId, TransactionId ORDER BY ClaimModificationId ASC) as OldValue,
m1.[NewValue],
m1.[UserEmail],
m1.[ModificationDate],
m1.[Action]
from #modifications m1

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