How to split a table into Master/Detail, with generating id SSIS? - sql

I stored a flat file into a table.
The tag P is for the master
The tag I is for the detail
Always a P row was precede with a (n) numbers of I rows.
The problem is, The I rows doesn't have an ID to join the P row.
Need to generate an ID to join the P row with the I rows.
The P row have a two fields that can be helpful.
The field "SequenceNumber" store the sequential ID.
The field "NumberOfItems" store how many I rows belong to a P Row.
I Need to split the data in two tables master/detail with identifiying ID.
I attach an image with the data
Thanks So Much
Original Table
RecordType SequenceNumber NumberOfItems TicketHeaderKey UnitID
P 1 3 ; 1
I 19900 0 FA 19900
I 3000 0 BK 3000
I 0 0 BK 0
P 2 1 ; 1
I 19900 0 FA 19900
P 3 2 ; 1
I 19900 0 FA 19900
I 3000 0 BK 3000
Need Split into two tables some like this
Master Table
RecordType SequenceNumber NumberOfItems TicketHeaderKey UnitID
P 1 3 ; 1
P 2 1 ; 1
P 3 2 ; 1
Detail Table
RecordType SequenceNumber idMasterTable TicketHeaderKey UnitID
I 19900 1 FA 19900
I 3000 1 BK 3000
I 0 1 BK 0
I 19900 2 FA 19900
I 19900 3 FA 19900
I 3000 3 BK 3000
SQL TABLE TO SPLIT

I'm not sure of the data you want to insert into each of the master and detail tables, but here is one way to do it.
For the MasterTable:
INSERT INTO MasterTable
SELECT
SequenceNumber AS id,
UnitId,
NumberOfItems,
TicketHeaderKey,
PrintType
FROM tabletosplit
WHERE RecordType = 'P'
ORDER BY SequenceNumber;
For the detail table:
WITH auxtable(id, RecordType, UnitId, SequenceNumber, NumberOfItems, TicketHeaderKey, PrintType, auxnumber, parentNumberOfItems, parentid)
AS (
SELECT 1, RecordType, UnitId, SequenceNumber, NumberOfItems, TicketHeaderKey, PrintType, 1, NumberOfItems, SequenceNumber
FROM tabletosplit WHERE SequenceNumber = 1
UNION ALL
SELECT
auxtable.id + 1,
T.RecordType, T.UnitId, T.SequenceNumber, T.NumberOfItems, T.TicketHeaderKey, T.PrintType,
CASE WHEN T.RecordType = 'P' THEN 1 else auxtable.auxnumber + 1 END,
CASE WHEN T.RecordType = 'P' THEN T.NumberOfItems ELSE auxtable.parentNumberOfItems END,
CASE WHEN T.RecordType = 'P' THEN T.SequenceNumber ELSE auxtable.parentid END
FROM auxtable
INNER JOIN (Select ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS id, RecordType, UnitId, SequenceNumber, NumberOfItems, TicketHeaderKey, PrintType
FROM tabletosplit
WHERE SequenceNumber <> 1) AS T
ON auxtable.id = T.id
)
INSERT INTO DetailTable
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS id, auxtable.UnitId , auxtable.TicketHeaderKey, auxtable.PrintType, auxtable.Parentid, auxtable.auxnumber -1 as DetailIndex
FROM auxtable
WHERE auxtable.RecordType = 'I';
Results:
Working sample here
Hope it helps you.

My primary concern is that you do not have a proper row sequence in the original table. Without this, this is no way to GTD the ordered data set.
In anticipation of you correcting this, I added a column called RowSeq
Example or dbFiddle
Declare #OriginalTable Table ([RowSeq] int,[RecordType] varchar(50),[SequenceNumber] int,[NumberOfItems] int,[TicketHeaderKey] varchar(50),[UnitID] varchar(50))
Insert Into #OriginalTable Values
(1,'P',1,3,';',1)
,(2,'I',19900,0,'FA',19900)
,(3,'I',3000,0,'BK',3000)
,(4,'I',0,0,'BK',0)
,(5,'P',2,1,';',1)
,(6,'I',19900,0,'FA',19900)
,(7,'P',3,2,';',1)
,(8,'I',19900,0,'FA',19900)
,(9,'I',3000,0,'BK',3000)
Select RecordType
,SequenceNumber
,NumberOfItems
,TicketHeaderKey
,UnitID
From #OriginalTable
Where RecordType='P'
Order By [RowSeq]
Select RecordType
,SequenceNumber
,IDMasterTable
,TicketHeaderKey
,UnitID
From (
Select *
,IDMasterTable = max(case when RecordType='P' then SequenceNumber end ) over (Order By RowSeq)
From #OriginalTable
) A
Where RecordType='I'
Order By [RowSeq]
Returns

Related

Get single row depending of conditional

I have a simple select query with some joins like:
SELECT
[c].[column1]
, [c].[column2]
FROM [Customer] AS [c]
INNER JOIN ...
So I do a left join with my principal table as:
LEFT JOIN [Communication] AS [com] ON [c].[CustomerGuid] = [com].[ComGuid]
this relatioship its 1 to *, one customer can have multiple communications
So in my select I want to get value 1 or 2 depending of condition:
Condition:
if ComTypeKey (from communication) table have a row with value 3 and have another row with vale 4 return 1 then 0
So I try something like:
SELECT
[c].[column1]
, [c].[column2]
, IIF([com].[ComTypeKey] = 3 AND [com].[ComTypeKey] = 4,1,0)
FROM [Customer] AS [c]
INNER JOIN ...
LEFT JOIN [Communication] AS [com] ON [c].[CustomerGuid] = [com].[ComGuid]
But it throws me two rows, beacause there are 2 rows on communication. My desire value is to get only one row with value 1 if my condition is true
If you have multiple rows you need GROUP BY, then count the relevant keys and subtract 1 to get (1, 0)
SELECT
[c].[column1]
, [c].[column2]
, COUNT(CASE WHEN [ComTypeKey] IN (3,4) THEN 1 END) - 1 as FLAG_CONDITION
FROM [Customer] AS [c]
INNER JOIN ...
LEFT JOIN [Communication] AS [com]
ON [c].[CustomerGuid] = [com].[ComGuid]
GROUP BY
[c].[column1]
, [c].[column2]
I'm not really sure I understand.
This will literally find if both values 3 and 4 exist for that CustomerGuid, and only select one of them in that case - not filtering out any record otherwise.
If this is not what you want, providing sample data with the expected result would remove the ambiguity.
SELECT Field1,
Field2,
...
FieldN
FROM (SELECT TMP.*,
CASE WHEN hasBothValues = 1 THEN
ROW_NUMBER() OVER ( PARTITION BY CustomerGuid ORDER BY 1 )
ELSE 1
END AS iterim_rn
FROM (SELECT TD.*,
MAX(CASE WHEN Value1 = '3' THEN 1 ELSE 0 END) OVER
( PARTITION BY CustomerGuid ) *
MAX(CASE WHEN Value1 = '4' THEN 1 ELSE 0 END) OVER
( PARTITION BY CustomerGuid ) AS hasBothValues
FROM TEST_DATA TD
) TMP
) TMP2
WHERE interim_rn = 1

SQL Query to view if a query contains both types

I am trying to create another column that will give me a Y if a Item number is located at both plants, or a N if it is only located at 1 plant.
Raw data:
ItemNum Item Plant
1 apple rightplant
2 orange leftplant
2 grape rightplant
1 apple left plant
Expected outcome:
ItemNum Item Plant PlantBoth
1 apple rightplant Y
2 orange leftplant N
2 grape rightplant N
1 apple left plant Y
Attempt(I'll probably need a case to justify a Y or N I would assume):
Select mi.ItemNum, mi.Item, mi.Plant,
(Select plant from myitems where itemnum = mi.itemnum and count(plant) > 1) as Plantboth
from myitems mi
You can use window functions:
select i.*,
(case when min(plant) over (partition by itemnum, item) <>
max(plant) over (partition by itemnum, item)
then 'Y'
else 'N'
end) as PlantBoth
from myitems i;
You could try it this way
--Create test data
declare #data table( itemNum int, Item varchar(20), Plant varchar(20))
insert into #data (itemNum, Item, Plant)
select 1, 'apple','rightplant' union
select 2, 'orange','rightplant' union
select 2, 'grape','leftplant' union
select 1, 'apple','leftplant'
--A left join returns all data from D1 and optionally D2 if there is a match.
--The case statement looks to see if there is a match.
select d1.*,
case when d2.itemNum is null then 'N' else 'Y' end as [Match]
from #data D1
left join #data D2
on d1.itemNum = d2.itemNum and d1.Item = D2.item and d1.plant != d2.plant
Group by itemnum, item to get the number of plants and join to the table:
select
m.*,
case g.counter when 2 then 'Y' else 'N' end plantboth
from myitems m inner join (
select itemnum, item, count(distinct plant) counter
from myitems
group by itemnum, item
) g on g.itemnum = m.itemnum and g.item = m.item

SQL Server case when or enum

I have a table something like:
stuff type price
first_stuff 1 43
second_stuff 2 46
third_stuff 3 24
fourth_stuff 2 12
fifth_stuff NULL 90
And for every type of stuff is assigned a description which is not stored in DB
1 = Bad
2 = Good
3 = Excellent
NULL = Not_Assigned
All I want is to return a table which count each type separately, something like:
Description Count
Bad 1
Good 2
Excellent 1
Not_Assigned 1
DECLARE #t TABLE ([type] INT)
INSERT INTO #t ([type])
VALUES (1),(2),(3),(2),(NULL)
SELECT
[Description] =
CASE t.[type]
WHEN 1 THEN 'Bad'
WHEN 2 THEN 'Good'
WHEN 3 THEN 'Excellent'
ELSE 'Not_Assigned'
END, t.[Count]
FROM (
SELECT [type], [Count] = COUNT(*)
FROM #t
GROUP BY [type]
) t
ORDER BY ISNULL(t.[type], 999)
output -
Description Count
------------ -----------
Bad 1
Good 2
Excellent 1
Not_Assigned 1
;WITH CTE_TYPE
AS (SELECT DESCRIPTION,
VALUE
FROM (VALUES ('BAD',
1),
('GOOD',
2),
('EXCELLENT',
3))V( DESCRIPTION, VALUE )),
CTE_COUNT
AS (SELECT C.DESCRIPTION,
Count(T.TYPE) TYPE_COUNT
FROM YOUR_TABLE T
JOIN CTE_TYPE C
ON T.TYPE = C.VALUE
GROUP BY TYPE,
DESCRIPTION
UNION ALL
SELECT 'NOT_ASSIGNED' AS DESCRIPTION,
Count(*) TYPE_COUNT
FROM YOUR_TABLE
WHERE TYPE IS NULL)
SELECT *
FROM CTE_COUNT
Hope, this helps.
SELECT ISNULL(D.descr, 'Not_Assigned'),
T2.qty
FROM
(SELECT T.type,
COUNT(*) as qty
FROM Table AS T
GROUP BY type) AS T2
LEFT JOIN (SELECT 1 as type, 'Bad' AS descr
UNION ALL
SELECT 2, 'Good'
UNION ALL
SELECT 3, 'Excellent') AS D ON D.type = T2.type
If you are using Sql server 2012+ use this
SELECT
[Description] = coalesce(choose (t.[type],'Bad','Good' ,'Excellent'), 'Not_Assigned'),
t.[Count]
FROM (
SELECT [type], [Count] = COUNT(*)
FROM yourtable
GROUP BY [type]
) t

Returning only id's of records that meet criteria

I need to return distinct ID's of records which meet following conditions :
must have records with field reason_of_creation = 1
and must NOT have records with field reason_of_creation = 0 or null
in the same time.
While i was able to do it, i keep wondering is there more elegant (even recommended) way of doing it.
Here is anonymized version of what i have :
select distinct st.some_id from (
select st.some_id, wanted.wanted_count as wanted, unwanted.unwanted_count as unwanted
from some_table st
left join (
select st.some_id, count(st.reason_of_creation) as wanted_count
from some_table st
where st.reason_of_creation=1
group by st.some_id
) wanted on wanted.some_id = st.some_id
left join (
select st.some_id, count(st.reason_of_creation) as unwanted_count
from some_table st
where st.reason_of_creation=0
group by st.some_id
) unwanted on unwanted.some_id = st.some_id
where wanted.wanted_count >0 and (unwanted.unwanted_count = 0 or unwanted.unwanted_count is null)
) st;
Sample data :
some_id reason_of_creation
1 1
1 0
2 1
3 null
4 0
4 1
5 1
desired result would be list of records with some_id = 2, 5
It seems to me your query is overkill,all you need is some post aggregation filtering
SELECT some_id FROM t
GROUP BY some_id
HAVING SUM(CASE WHEN reason_of_creation = 1 THEN 1 ELSE 0 END)>0
AND SUM(CASE WHEN reason_of_creation = 0 OR reason_of_creation IS NULL THEN 1 ELSE 0 END)=0
I think that more elegant query exists and it is based on assumption what reasoson_of_crdeation field is integer, so minimal possible it's value, which greater than 0 is 1
This is for possible negative values for reasoson_of_crdeation:
select someid from st
where reasoson_of_crdeation != -1
group by someid
having(min(nvl(abs(reasoson_of_crdeation), 0)) = 1)
or
select someid from st
group by someid
having(min(nvl(abs(case when reasoson_of_crdeation = -1 then -2 else reasoson_of_crdeation end), 0)) = 1)
And this one in a case if reasoson_of_crdeation is non-negative integer:
select someid from st
group by someid
having(min(nvl(reasoson_of_crdeation, 0)) = 1)

SQL: SUMming certain items in a column and subtracting it from another figure in that column

Sorry about the title. It might be a bit confusing! The sample table I'm working with is given below:
ID Quantity Type
-----------------------------------------------
1 14 PO
1 2 PO
1 4 MH
1 3 MH
1 2 MH
2 16 PO
2 12 MH
2 9 MH
Here's what I want to do. I want to sum all quantities of ID = 1 and Type = PO (14 + 2) as SUM_IN. I then want to sum all quantities of ID = 1 and Type = MH (4 + 3 + 2) as SUM_OUT. Once I have this done I want to compare the two and return values only where SUM_OUT > SUM_IN. So for ID = 1 would not be returned where as ID = 2 would, because (12 + 9) > 16.
Is there a way to do this in SQL or will I need to use PL/SQL and variables for the task. I have very little experience in PL/SQL, but logically it seems that variables would be the easiest way to solve the problem. I know that select statements can be stored in variables but I'm not sure how to. Here are my two SQL selects anyway
SELECT SUM(QUANTITY) AS SUM_IN
FROM TRANSLOG
WHERE TYPE IN ('PO')
AND ID = '1'
SELECT SUM(QUANTITY) AS SUM_OUT
FROM TRANSLOG
WHERE TYPE IN ('MH')
AND ID = '1'
So if I could set both these to variables, the task shouldn't be too difficult, right???
Thanks in advance for the help.
select ID,
sum ( Quantity * case Type when 'po' then 1 else 0 end ) as SUM_IN,
sum ( Quantity * case Type when 'mh' then 1 else 0 end ) as SUM_OUT
from translog
group by ID
having sum ( Quantity * case Type when 'po' then 1 else -1 end ) < 0
As you have tagged you question with plsql tag I assume that the RDBMS you are goint to execute query against is Oracle. If so, then here is another approach(using DECODE function
) to get the result set you want.
select *
from (select id
, sum(Quantity*decode(tp, 'PO', 1, 0)) as sum_in
, sum(Quantity*decode(tp, 'MH', 1, 0)) as sum_out
from t1
group by id
order by id )
where sum_out > sum_in
Result:
ID SUM_IN SUM_OUT
-----------------------
2 16 21
If you want to display the rest of the columns along with sum_in, sum_out the following query might be in handy:
select id
, quantity
, Tp
, sum_in
, sum_out
from (select id
, quantity
, tp
, sum(Quantity*decode(tp, 'PO', 1, 0)) over(partition by id) as sum_in
, sum(Quantity*decode(tp, 'MH', 1, 0)) over(partition by id) as sum_out
from t1
)
where sum_out > sum_in
Result:
Id Quantity Tp Sum_In Sum_Out
---------------------------------------------
2 16 PO 16 21
2 12 MH 16 21
2 9 MH 16 21
SELECT CASE WHEN b.SUM_OUT > a.SUM_IN then b.SUM_OUT else '' END as SUM_OUT,
CASE WHEN b.SUM_OUT > a.SUM_IN then a.SUM_IN else '' END as SUM_IN
FROM
(SELECT ID,SUM(QUANTITY) AS SUM_IN
FROM TRANSLOG
WHERE TYPE IN ('PO')
AND ID = '1'
GROUP BY ID,Type
) a
INNER JOIN
(SELECT ID,SUM(QUANTITY) AS SUM_OUT
FROM TRANSLOG
WHERE TYPE IN ('MH')
AND ID = '1'
GROUP BY ID,Type
) b
ON a.ID=b.ID