How to consolidate two sql server tables using Group By clause? - sql

I have two tables TableA and TableB as follows:
TableA:
ItemID Qty Rate
-------- ----- --------
1 10 100.00
2 20 150.00
TableB:
ItemID Qty Rate
-------- ----- -------
1 5 150.00
3 10 200.00
3 20 400.00
Now I want to consolidate these two tables. My desired result needs to be as follows:
Result TableA:
ItemID Qty Rate
-------- ----- -------
1 15 150.00
2 20 150.00
3 30 400.00
I tried the following Insert Select statement, But it does not give the desired result.
INSERT INTO TableA
(
ItemID,
Qty,
Rate
)
SELECT
ItemID,
SUM(Qty),
MAX(Rate)
FROM
TableB
GROUP BY
ItemID
But it gives the result as follows:
ItemID Qty Rate
-------- ----- --------
1 10 100.00
2 20 150.00
1 5 150.00
3 30 400.00
How to achieve my desired result?
I tried like this:
MERGE PUR_PODetail AS Target
USING (
SELECT
#POID,
ItemID,
SUM(POQuantity),
MAX(UnitRate),
1,
CASE WHEN D1 = '' THEN NULL ELSE D1 END D1,
CASE WHEN D2 = '' THEN NULL ELSE D2 END D2,
CASE WHEN D3 = '' THEN NULL ELSE D3 END D3,
CASE WHEN RandomDimension = '' THEN NULL ELSE RandomDimension END RandomDimension,
0
FROM
#Detail
GROUP BY
ItemID, D1, D2, D3, RandomDimension
) AS Source ON (Target.ItemID = Source.ItemID) AND
(ISNULL(Target.D1, -999) = ISNULL(Source.D1, -999)) AND
(ISNULL(Target.D2, -999) = ISNULL(Source.D2, -999)) AND
(ISNULL(Target.D3, -999) = ISNULL(Source.D3, -999)) AND
(ISNULL(Target.RandomDimension, -999) = ISNULL(Source.RandomDimension, -999))
WHEN MATCHED
THEN UPDATE SET
Target.POQuantity = Target.POQuantity + Source.POQuantity,
Target.UnitRate = MAX(Source.UnitRate)
WHEN NOT MATCHED
INSERT
(
POID,
ItemID,
POQuantity,
UnitRate,
ItemStatusID,
D1,
D2,
D3,
RandomDimension,
EDInclusive_f
)
VALUES
(
#POID,
Source.ItemID,
Source.POQuantity,
Source.UnitRate,
1,
CASE WHEN Source.D1 = '' THEN NULL ELSE Source.D1 END D1,
CASE WHEN Source.D2 = '' THEN NULL ELSE Source.D2 END D2,
CASE WHEN Source.D3 = '' THEN NULL ELSE Source.D3 END D3,
CASE WHEN Source.RandomDimension = '' THEN NULL ELSE Source.RandomDimension END RandomDimension,
0
)
But it gives the following error.
Please correct the error. I dont know what would be wrong here.
Msg 102, Level 15, State 1, Procedure PUR_PurchaseOrder_IU, Line 936
Incorrect syntax near 'MERGE'.
Msg 156, Level 15, State 1, Procedure PUR_PurchaseOrder_IU, Line 953
Incorrect syntax near the keyword 'AS'.
But when I remove these merge statement from my stored procedure, it is executing...

You can't just use the INSERT statement for this, you have to either INSERTor UPDATEdepending on the ItemID already being present in your target table.
SQL Server 2005
UPDATE #TableA
SET Qty = a.Qty + b.Qty
, Rate = CASE WHEN a.Rate < b.Rate
THEN b.Rate
ELSE a.Rate
END
FROM #TableA a
INNER JOIN (
SELECT ItemID
, Qty = SUM(Qty)
, Rate = MAX(Rate)
FROM #TableB
GROUP BY
ItemID
) b ON a.ItemID = b.ItemID
INSERT INTO #TableA
SELECT ItemID, Qty, Rate
FROM ( SELECT ItemID
, Qty = SUM(Qty)
, Rate = MAX(Rate)
FROM #TableB b
WHERE NOT EXISTS (SELECT * FROM #TableA a WHERE a.ItemID = b.ItemID)
GROUP BY
ItemID
) b
SQL Server 2008 provides the MERGE statement for this.
Performs insert, update, or delete operations on a target table based on the results of a join with a source table. For example, you
can synchronize two tables by inserting, updating, or deleting rows in
one table based on differences found in the other table.
SQL Server 2008
MERGE #TableA AS Target
USING (
SELECT ItemID
, Qty = SUM(Qty)
, Rate = MAX(Rate)
FROM #TableB
GROUP BY
ItemID
) AS source (ItemID, Qty, Rate) ON (target.ItemID = source.ItemID)
WHEN MATCHED THEN
UPDATE SET target.Qty = target.Qty + source.Qty
, target.Rate = CASE WHEN target.Rate < source.Rate
THEN source.Rate
ELSE target.Rate
END
WHEN NOT MATCHED THEN
INSERT (ItemID, Qty, Rate)
VALUES (source.ItemID, source.Qty, source.Rate);

Try this one:
MERGE TableA T
USING
(
SELECT ItemId, SUM(Qty) Qty, MAX(Rate) Rate
FROM
(
SELECT ItemId, Qty, Rate from TableA
UNION ALL
SELECT ItemId, Qty, Rate from TableB
) S
ON T.ItemId = S.ItemId
WHEN MATCHED THEN UPDATE SET
Qty = S.Qty,
Rate= S.Rate
WHEN NOT MATCHED THEN
INSERT(ItemId, Qty, Rate)
VALUES(S.ItemId, S.Qty, S.Rate);

declare #t table (ItemID int,Qty int,Rate int)
insert into #t values (1,10,100),(2,20,150)
Select * from #t
declare #t1 table (ItemID int,Qty int,Rate int)
insert into #t1 values (1,5,150),(3,10,200),(3,20,400)
Select * from #t1
insert into #t
Select ItemID,sum(Qty) Qty,max(Rate) Rate from #t1 where ItemID not in (Select ItemID from #t)
group by ItemID
Select * from #t

Related

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

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

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

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

SQL SERVER T-SQL Calculate SubTotal and Total by group

I am trying to add subtotal by group and total to a table. I've recreated the data using the following sample.
DECLARE #Sales TABLE(
CustomerName VARCHAR(20),
LegalID VARCHAR(20),
Employee VARCHAR(20),
DocDate DATE,
DocTotal Int,
DueTotal Int
)
INSERT INTO #Sales SELECT 'Jhon Titor','12345', 'Employee1','2015-09-01',1000,200
INSERT INTO #Sales SELECT 'Jhon Titor','12345', 'Employee1','2015-08-20',500,100
INSERT INTO #Sales SELECT 'Jhon Titor','12345', 'Employee1','2015-08-18',200,50
INSERT INTO #Sales SELECT 'Deli Armstrong','2345', 'Employee1','2015-09-17',2300,700
INSERT INTO #Sales SELECT 'Deli Armstrong','2345', 'Employee1','2015-09-11',5000,1000
INSERT INTO #Sales SELECT 'Ali Mezzu','6789', 'Employee1','2015-09-07',300,200
Selecting #Sales
I need to add the customer subtotal just below customer occurrences and total in the end row of table like this:
what I've tried so far:
select
case
when GROUPING(CustomerName) = 1 and
GROUPING(Employee) = 1 and
GROUPING(DocDate) = 1 and
GROUPING(LegalID) = 0 then 'Total ' + CustomerName
when GROUPING(CustomerName) = 1 and
GROUPING(Employee) = 1 and
GROUPING(DocDate) =1 and
GROUPING(LegalID) = 1 then 'Total'
else CustomerName end as CustomerName,
LegalID, Employee,DocDate,
sum(DocTotal) as DocTotal,
sum(DueTotal) as DueTotal
From #Sales
group by LegalID, CustomerName,Employee,DocDate with rollup
But I am getting subtotal as null where it should say Total Jhon Titor as I set it static in the query, also it is repeated for every not aggregated column (3),
How can I add subtotal and total to the table presented above?
I am open to use a query without ROLLUP operator. I think it is possible using unions but don't know how to start.
Thanks for considering my question.
I think this is what you want:
select (case when GROUPING(CustomerName) = 0 and
GROUPING(Employee) = 1 and
GROUPING(DocDate) = 1 and
GROUPING(LegalID) = 1
then 'Total ' + CustomerName
when GROUPING(CustomerName) = 1 and
GROUPING(Employee) = 1 and
GROUPING(DocDate) =1 and
GROUPING(LegalID) = 1 then 'Total'
else CustomerName
end) as CustomerName,
LegalID, Employee,DocDate,
sum(DocTotal) as DocTotal,
sum(DueTotal) as DueTotal
From #Sales
group by grouping sets((LegalID, CustomerName ,Employee, DocDate),
(CustomerName),
()
);
You can use the following query:
SELECT CustomerName, LegalID, Employee, DocDate, DocTotal, DueTotal
FROM (
SELECT CustomerName AS cName, CustomerName,
LegalID, Employee, DocDate, DocTotal, DueTotal,
1 AS ord
FROM Sales
UNION ALL
SELECT CustomerName AS cName, CONCAT('Total ', CustomerName),
NULL, NULL, NULL,
SUM(DocTotal), SUM(DueTotal), 2 AS ord
FROM Sales
GROUP BY CustomerName
UNION ALL
SELECT 'ZZZZ' AS cName, 'Total', NULL, NULL, NULL,
SUM(DocTotal), SUM(DueTotal), 3 AS ord
FROM Sales ) AS t
ORDER BY cName, ord
Demo here

SQL join 1 line and return NULLs for others

Is it possible to stop SQL from duplicating lines from a table when creating a JOIN to a table with more than one line?
Table 1
Car Name Colour Size
Car 1 Red big
car 2 Blue small
Car 3 Green small
Table 2
Car Name Part Number
Car 1 123456
Car 1 234567
Car 1 345678
Car 2 ABCDEFG
Car 2 BCDEFGH
Car 2 CDEFGHI
Then Join Table 1 with Table 2 on "Car Name" but only have the information once from each table,
Resulting SQL View
Car Name Colour Size Part Number
Car 1 Red big 123456
NULL NULL NULL 234567
NULL NULL NULL 345678
Car 2 Blue small ABCDEFG
NULL NULL NULL BCDEFGH
NULL NULL NULL CDEFGHI
edit: if the original "Car Name" column is duplicated this isn't a problem, that's not really made clear above because i've put NULL's under that column but i understand that's the column its joined on and that information is already on the lines of the second table, its more being able to stop the duplication of the other information that isn't in table 2
As mentioned in comments it is recommend to do this kind of data formatting on client application but still if need a sql answer then try something like this.
WITH cte
AS (SELECT RN=Row_number() over(PARTITION BY a.Car_Name ORDER BY (SELECT NULL)),
a.car_name,
a.color,
a.size,
b.part_number
FROM table1
INNER JOIN table2
ON a.car_name = b.car_name)
SELECT car_name=CASE WHEN rn = 1 THEN car_name ELSE NULL END,
color=CASE WHEN rn = 1 THEN color ELSE NULL END,
size=CASE WHEN rn = 1 THEN size ELSE NULL END,
part_number
FROM cte
You would just need to assign a ROW_NUMBER() and populate table1 fields only when row number = 1 something like this:
WITH q1
AS (
SELECT t2.*
,row_number() OVER (
PARTITION BY t2.CarName ORDER BY PartNumber
) AS rn
FROM table2 t2
)
SELECT CASE
WHEN q1.rn = 1
THEN t1.CarName
ELSE NULL
END AS CarName
,CASE
WHEN q1.rn = 1
THEN t1.Colour
ELSE NULL
END AS Colour
,CASE
WHEN q1.rn = 1
THEN t1.Size
ELSE NULL
END AS Size
,q1.PartNumber
FROM q1
INNER JOIN table1 t1 ON t1.carname = q1.carname
SQL Fiddle Demo
try,
declare #t table(CarName varchar(10),Colour varchar(10), Size varchar(10))
insert into #t (carname,Colour,Size) values
('Car 1', 'Red', 'big'),
('car 2','Blue','small'),
('car 3','Gr een','small')
declare #t1 table(CarName varchar(10),part varchar(10))
insert into #t1 (carname,part) values
('car 1','123456'),
('car 1','2345670'),
('car 1','345678'),
('car 2','ABCDEFG'),
('car 2','BcDEFGH'),
('car 2','cDEFGHI')
select t.*, t1.part from #t t right join
(select carname,part, row_number() over (partition by carname order by part) rno from #t1)
t1 on t.CarName=(case when t1.rno=1 then t1.CarName else null end)