The fiddle:
CREATE TABLE person
([first_name] varchar(10), [surname] varchar(10), [date_of_birth] date, [person_id] int);
INSERT INTO person
([first_name], [surname], [date_of_birth] ,[person_id])
VALUES
('Alice', 'AA', '1/1/1990', 1),
('Bob' , 'BB', '1/1/1990', 3),
('Carol', 'CC', '1/1/1990', 4),
('Kate' , 'KK', '1/1/1990', 7);
CREATE TABLE person_membership
([person_id] int, [status_flag] varchar(1), [membership_id] int);
INSERT INTO person_membership
([person_id], [status_flag], [membership_id])
VALUES
(1, 'A', 10),
(1, 'A', 20),
(3, 'A', 30),
(4, 'A', 40),
(7, 'A', 60),
(7, 'T', 70);
CREATE TABLE memship
([membership_id] int, [memship_status] varchar(1));
INSERT INTO memship
([membership_id], [memship_status])
VALUES
(10, 'A'),
(20, 'A'),
(30, 'A'),
(40, 'A'),
(50, 'T'),
(60, 'A'),
(70, 'A');
The query:
WITH t AS
(SELECT first_name, surname, date_of_birth, p.person_id, m.membership_id
FROM person p
INNER JOIN person_membership pm ON p.person_id=pm.person_id
INNER JOIN memship m ON pm.membership_id = m.membership_id
WHERE pm.status_flag='A' and m.memship_status='A')
SELECT t.first_name, t.surname, t.date_of_birth, t.person_id, t1.membership_id
FROM t
INNER JOIN t t1 ON t.person_id=t1.person_id
GROUP BY t.first_name, t.surname, t.date_of_birth, t.person_id, t1.membership_id
HAVING count(*) > 1
The problem:
Find and display only those reconds marked as active and with multiple membership IDs assigned to one person id.
The expected outcome:
The question:
My query works fine and gives me the expected outcome but the execution plan looks rather convoluted. What are the better, more elegant, expert-recommended ways of doing it?
Seems like you don't need that big GROUP BY at all, you could use a windowed function inside the CTE instead:
WITH Counts AS(
SELECT p.first_name,
p.surname,
p.date_of_birth,
p.person_id,
m.membership_id,
COUNT(*) OVER (PARTITION BY p.person_id) AS PersonMemCount
FROM person p
INNER JOIN person_membership pm ON p.person_id=pm.person_id
INNER JOIN memship m ON pm.membership_id = m.membership_id
WHERE pm.status_flag='A'
AND m.memship_status='A')
SELECT C.first_name,
C.surname,
C.date_of_birth,
C.person_id,
C.membership_id
FROM Counts C
WHERE C.PersonMemCount > 1;
Related
I've a dataset similar to the one below.
I need to update the base lookup table based on the values provided in the updated_CustomerId column. The base tables is the same as the dataset but it does not have updated_CustomerId column.
The challenge here that the base table has a unique constraint based on combination of three columns below:
Current_CustomerID
Order_ID
OrderCategory
DESIRED OUTPUT:
After the update either one of Old_customerIds (17360410 - Pk 8, 21044488 - Pk = 9) can be reassigned to the Update_CustomerID
PrimaryKey 2 will not updated as that would lead to Unique constraint violation, but it will then be deleted along with one of the PrimaryKeys from the above either 8 or 9, depending on which one was updated (re-assigned to the new id)
After everything is updated on the base table I then delete from the base table all records where Current_CustomerID was not re-assigned to the updated_CustomerId (if different)
IF OBJECT_ID('tempdb..#DataSet') IS NOT NULL
DROP TABLE #DataSet
IF OBJECT_ID('tempdb..#BaseTable') IS NOT NULL
DROP TABLE #BaseTable
CREATE TABLE #DataSet
(
PrimaryKey INT NOT NULL CONSTRAINT [PK_dataset_ID] PRIMARY KEY,
Current_CustomerID INT NOT NULL,
Order_ID INT NOT NULL,
OrderCategory VARCHAR(50) NOT NULL,
Updated_CustomerId INT NOT NULL
)
INSERT INTO #DataSet (PrimaryKey, Current_CustomerID, Order_ID, OrderCategory, updated_CustomerId)
VALUES
(1, 17395001, 4451784, 'Kitchen', 25693110),
(2, 25693110, 4451784, 'Kitchen', 25693110),
(3, 25693110, 2083059, 'Kitchen', 25693110),
(4, 25693110, 2163679, 'Kitchen', 25693110),
(5, 25693110, 2171466, 'Kitchen', 25693110),
(6, 25693110, 2163679, 'Bathroom', 25693110),
(7, 25693110, 2171466, 'Bathroom', 25693110),
(8, 17360410, 3377931, 'Furniture', 16303984),
(9, 21044488, 3377931, 'Furniture', 16303984),
(10, 1534323, 2641714, 'Furniture', 16303984),
(11, 16303984, 2641726, 'Furniture', 16303984),
(12, 16303984, 2641793, 'Furniture', 16303984),
(13, 16303984, 2641816, 'Furniture', 16303984),
(14, 16303345, 2641816, 'Garden', 16301239),
(15, 12345678, 1239065, 'Medicine', 1075432)
CREATE TABLE #BaseTable
(
PrimaryKey INT NOT NULL CONSTRAINT [PK_baseTable_ID] PRIMARY KEY,
CustomerID INT NOT NULL,
Order_ID INT NOT NULL,
OrderCategory VARCHAR(50) NOT NULL,
)
CREATE UNIQUE NONCLUSTERED INDEX [IDX_LookUp] ON #BaseTable
(
CustomerID ASC,
Order_ID ASC,
OrderCategory ASC
) ON [PRIMARY]
INSERT INTO #BaseTable (PrimaryKey, CustomerID, Order_ID, OrderCategory)
VALUES
(1, 17395001, 4451784, 'Kitchen'),
(2, 25693110, 4451784, 'Kitchen'),
(3, 25693110, 2083059, 'Kitchen'),
(4, 25693110, 2163679, 'Kitchen'),
(5, 25693110, 2171466, 'Kitchen'),
(6, 25693110, 2163679, 'Bathroom'),
(7, 25693110, 2171466, 'Bathroom'),
(8, 17360410, 3377931, 'Furniture'),
(9, 21044488, 3377931, 'Furniture'),
(10, 1534323, 2641714, 'Furniture'),
(11, 16303984, 2641726, 'Furniture'),
(12, 16303984, 2641793, 'Furniture'),
(13, 16303984, 2641816, 'Furniture'),
(14, 16303345, 2641816, 'Garden'),
(15, 12345678, 1239065, 'Medicine')
-- select * from #BaseTable
-- select * from #DataSet
; with CTE AS (
select a.*
,rank() over (partition by a.updated_CustomerId, a.Order_ID, a.OrderCategory
order by a.Current_CustomerID) as flag
from #DataSet a
)
with CTE AS (
select a.*
,rank() over (partition by a.updated_CustomerId, a.Order_ID, a.OrderCategory order by a.Current_CustomerID) as flag
from #DataSet a
)
update b
set CustomerID = a.Updated_CustomerId
from #BaseTable b
inner join CTE a on b.PrimaryKey = a.PrimaryKey
where flag <> 2
Msg 2601, Level 14, State 1, Line 82
Cannot insert duplicate key row in object 'dbo.#BaseTable' with unique index 'IDX_LookUp'. The duplicate key value is (25693110, 4451784, Kitchen).
The statement has been terminated.
I think you just want to get a row_number for the #DataTable, and then delete where there are more than one based on the unique key:
//...
DELETE bt
FROM #BaseTable bt
INNER JOIN (
SELECT a.PrimaryKey,
a.Updated_CustomerId,
a.Order_ID,
a.OrderCategory,
row = ROW_NUMBER() OVER (PARTITION BY a.Updated_CustomerId, a.Order_ID, a.OrderCategory ORDER BY a.Current_CustomerID)
FROM #BaseTable b
INNER JOIN #DataSet a
ON b.PrimaryKey = a.PrimaryKey
) x
ON bt.PrimaryKey = x.PrimaryKey
AND x.row > 1
I have this table structure and query written with the help of some guy here and it works perfect. I want to get the opening and closing balance between the dates. I have commented the date on which I want to get the date and if I run that date check the expected output I want is shown below.
Here is the structure and sample data:
DROP TABLE [TransactionMaster];
DROP TABLE [VoucherType];
CREATE TABLE [VoucherType](
[VoucherTypeCode] [tinyint] NOT NULL PRIMARY KEY,
[FullName] [nvarchar](255) NOT NULL
);
INSERT INTO [VoucherType] VALUES (1, 'Cash Payment Voucher');
INSERT INTO [VoucherType] VALUES (2, 'Cash Receipt Voucher');
INSERT INTO [VoucherType] VALUES (3, 'Bank Payment Voucher');
INSERT INTO [VoucherType] VALUES (4, 'Bank Receipt Voucher');
CREATE TABLE [TransactionMaster](
[ID] [bigint] NOT NULL PRIMARY KEY,
[VoucherTypeCode] [tinyint] NOT NULL,
[PayeeName] [varchar](255) NOT NULL,
[TransactionDate] datetime,
[RefNo] [nvarchar](50) NULL
CONSTRAINT [FK_tbl_TransactionMaster_tbl_VoucherType] FOREIGN KEY([VoucherTypeCode])
REFERENCES [VoucherType] ([VoucherTypeCode])
)
INSERT INTO [TransactionMaster] VALUES (1, 2, 'Asim', '2018-03-21', 'CRV-0001-LHR');
INSERT INTO [TransactionMaster] VALUES (2, 4, 'Ali', '2018-03-21', 'BRV-2421-KHI');
INSERT INTO [TransactionMaster] VALUES (3, 1, 'Erick', '2018-03-23', 'CPV-5435-ISL');
INSERT INTO [TransactionMaster] VALUES (4, 3, 'Asim', '2018-03-24', 'BPV-2345-CAN');
INSERT INTO [TransactionMaster] VALUES (5, 2, 'Mehboob', '2018-03-25', 'CRV-2976-PSH');
INSERT INTO [TransactionMaster] VALUES (6, 1, 'Erick', '2018-03-25', 'CPV-2323-KOH');
INSERT INTO [TransactionMaster] VALUES (7, 1, 'Feroze', '2018-03-21', 'CRV-0531-SRG');
INSERT INTO [TransactionMaster] VALUES (8, 3, 'Ali', '2018-03-21', 'BRV-2001-RWP');
CREATE TABLE TransactionDetail
(
ID NUMERIC NOT NULL PRIMARY KEY,
TransactionCode bigint,
DrAmount NUMERIC,
CrAmount NUMERIC
);
INSERT INTO TransactionDetail VALUES (1, 1, '2500', NULL);
INSERT INTO TransactionDetail VALUES (2, 1, NULL, '1500');
INSERT INTO TransactionDetail VALUES (3, 1, NULL, '1000');
INSERT INTO TransactionDetail VALUES (4, 2, '1150', NULL);
INSERT INTO TransactionDetail VALUES (5, 2, NULL, '1150');
INSERT INTO TransactionDetail VALUES (6, 3, '600', NULL);
INSERT INTO TransactionDetail VALUES (7, 3, '400', NULL);
INSERT INTO TransactionDetail VALUES (8, 3, '200', NULL);
INSERT INTO TransactionDetail VALUES (9, 3, NULL, '1200');
INSERT INTO TransactionDetail VALUES (10, 4, '1000', NULL);
INSERT INTO TransactionDetail VALUES (11, 4, NULL, '1000');
INSERT INTO TransactionDetail VALUES (12, 5, '2400', NULL);
INSERT INTO TransactionDetail VALUES (13, 5, NULL, '1200');
INSERT INTO TransactionDetail VALUES (14, 5, NULL, '1000');
INSERT INTO TransactionDetail VALUES (15, 5, NULL, '200');
INSERT INTO TransactionDetail VALUES (16, 6, '2900', NULL);
INSERT INTO TransactionDetail VALUES (17, 6, NULL, '2900');
INSERT INTO TransactionDetail VALUES (18, 7, '700', NULL);
INSERT INTO TransactionDetail VALUES (19, 7, '300', NULL);
INSERT INTO TransactionDetail VALUES (20, 7, '2100', NULL);
INSERT INTO TransactionDetail VALUES (21, 7, NULL, '3100');
INSERT INTO TransactionDetail VALUES (22, 8, '500', NULL);
INSERT INTO TransactionDetail VALUES (23, 8, NULL, '500');
Here is the query
with data1 as (
select a.id inid,a.VoucherTypeCode,PayeeName,MAX(c.DrAmount) InAmount,TransactionDate,RefNo,FullName from TransactionMaster a
inner join [VoucherType] b on a.VoucherTypeCode = b.VoucherTypeCode
inner join TransactionDetail c on a.ID = c.TransactionCode
where a.VoucherTypeCode in (1,3)
GROUP BY a.id,a.VoucherTypeCode,PayeeName,TransactionDate,RefNo,FullName
),
data2 as (
select a.id outid,a.VoucherTypeCode,PayeeName,MAX(c.CrAmount) OutAmount,TransactionDate,RefNo,FullName from TransactionMaster a
inner join [VoucherType] b on a.VoucherTypeCode = b.VoucherTypeCode
inner join TransactionDetail c on a.ID = c.TransactionCode
where a.VoucherTypeCode in (2,4)
GROUP BY a.id,a.VoucherTypeCode,PayeeName,TransactionDate,RefNo,FullName
)
select *,COALESCE(a.TransactionDate,b.TransactionDate) as FullDate from data1 a full join data2 b on inid = outid and a.TransactionDate = b.TransactionDate
--WHERE COALESCE(a.TransactionDate,b.TransactionDate) BETWEEN '2018-03-23 00:00:00.000' AND '2018-03-24 00:00:00.000'
order by FullDate
The expected output is provided below when you remove the commenting from the date check:
inid VoucherTypeCode PayeeName InAmount TransactionDate RefNo FullName outid VoucherTypeCode PayeeName OutAmount TransactionDate RefNo FullName FullDate Opening
-------------------- --------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------- ----------------------- -------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------- --------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------- ----------------------- -------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------
3 1 Erick 1200 2018-03-23 00:00:00.000 CPV-5435-ISL Cash Payment Voucher NULL NULL NULL NULL NULL NULL NULL 2018-03-23 00:00:00.000 -50
4 3 Asim 1000 2018-03-24 00:00:00.000 BPV-2345-CAN Bank Payment Voucher NULL NULL NULL NULL NULL NULL NULL 2018-03-24 00:00:00.000 1150
The formula is that (Opening+InAmount) - Out Amount will be the Closing Balance and Opening will be previous rows Closing Balance.
So for first record opening will be 0 and closing will be 3100 and for second opening = 3100 and closing 3600 and so on.
If the VoucherType is 1 or 3 than I have to get the Credit Amount CrAmount and if the VoucherType is 2 or 4 then I need to get the Debit Amount DrAmount.
I've asked this question before and got the expected output as well but now the DB structure is changed a bit and I am unable to use that logic here.
basically it is the same as the previous query that i posted in your other thread.
Since the details in the other table TransactionDetail, you just need to INNER JOIN from the TransactionMaster to it and do the SUM(). I didn't verify the Dr - Cr or Cr - Dr logic. Please verify yourself.
SELECT t.*, v.FullName, o.Opening
FROM [TransactionMaster] t
INNER JOIN [VoucherType] v on t.VoucherTypeCode = v.VoucherTypeCode
OUTER APPLY
(
SELECT Opening = sum(case when m.[VoucherTypeCode] in (1, 3)
then - ISNULL(d.CrAmount, 0)
else + ISNULL(d.CrAmount, 0)
end)
FROM [TransactionMaster] m
INNER JOIN [TransactionDetail] d ON m.ID = d.TransactionCode
WHERE m.TransactionDate < t.TransactionDate
) o
WHERE t.TransactionDate BETWEEN '2018-03-23' AND '2018-03-24'
order by t.TransactionDate
EDIT:
You don't need to use FULL JOIN to identify IN and OUT. Just use CASE statement as below
SELECT inID = case when t.VoucherTypeCode in (1,3) then t.ID end,
inAmount = case when t.VoucherTypeCode in (1,3) then a.Amount end,
outID = case when t.VoucherTypeCode in (2,4) then t.ID end,
outAmount = case when t.VoucherTypeCode in (2,4) then a.Amount end,
t.PayeeName, t.TransactionDate, t.RefNo,
v.FullName, Opening = isnull(o.Opening, 0)
FROM [TransactionMaster] t
INNER JOIN [VoucherType] v on t.VoucherTypeCode = v.VoucherTypeCode
CROSS APPLY
(
SELECT Amount = sum(case when m.[VoucherTypeCode] in (1, 3)
then -CrAmount
else DrAmount
end)
FROM [TransactionMaster] m
INNER JOIN [TransactionDetail] d ON m.ID = d.TransactionCode
WHERE m.ID = t.ID
) a
OUTER APPLY
(
SELECT Opening = sum(case when m.[VoucherTypeCode] in (1, 3)
then -CrAmount
else DrAmount
end)
FROM [TransactionMaster] m
INNER JOIN [TransactionDetail] d ON m.ID = d.TransactionCode
WHERE m.TransactionDate < t.TransactionDate
) o
order by t.TransactionDate
I have a simple categories table as with the following columns:
Id
Name
ParentId
So, an infinite amount of Categories can be the child of a category. Take for example the following hierarchy:
I want, in a simple query that returns the category "Business Laptops" to also return a column with all it's parents, comma separator or something:
Or take the following example:
Recursive cte to the rescue....
Create and populate sample table (Please save us this step in your future questions):
DECLARE #T as table
(
id int,
name varchar(100),
parent_id int
)
INSERT INTO #T VALUES
(1, 'A', NULL),
(2, 'A.1', 1),
(3, 'A.2', 1),
(4, 'A.1.1', 2),
(5, 'B', NULL),
(6, 'B.1', 5),
(7, 'B.1.1', 6),
(8, 'B.2', 5),
(9, 'A.1.1.1', 4),
(10, 'A.1.1.2', 4)
The cte:
;WITH CTE AS
(
SELECT id, name, name as path, parent_id
FROM #T
WHERE parent_id IS NULL
UNION ALL
SELECT t.id, t.name, cast(cte.path +','+ t.name as varchar(100)), t.parent_id
FROM #T t
INNER JOIN CTE ON t.parent_id = CTE.id
)
The query:
SELECT id, name, path
FROM CTE
Results:
id name path
1 A A
5 B B
6 B.1 B,B.1
8 B.2 B,B.2
7 B.1.1 B,B.1,B.1.1
2 A.1 A,A.1
3 A.2 A,A.2
4 A.1.1 A,A.1,A.1.1
9 A.1.1.1 A,A.1,A.1.1,A.1.1.1
10 A.1.1.2 A,A.1,A.1.1,A.1.1.2
See online demo on rextester
I want to get same output:
using the following sample data
create table x
(
id int,
date datetime,
stat int
)
insert into x
values (1, '2017-01-01', 100), (1, '2017-01-03', 100), (1, '2017-01-05', 100),
(1, '2017-01-07', 150), (1, '2017-01-09', 150), (1, '2017-02-01', 150),
(1, '2017-02-02', 100), (1, '2017-02-12', 100), (1, '2017-02-15', 100),
(1, '2017-02-17', 150), (1, '2017-03-09', 150), (1, '2017-03-11', 150),
(2, '2017-01-01', 100), (2, '2017-01-03', 100), (2, '2017-01-05', 100),
(2, '2017-01-07', 150), (2, '2017-01-09', 150), (2, '2017-02-01', 150),
(2, '2017-02-02', 100), (2, '2017-02-12', 100), (2, '2017-02-15', 100),
(2, '2017-02-17', 150), (2, '2017-03-09', 150), (2, '2017-03-11', 150)
I tried to use something like this
with a as
(
select
id, date,
ROW_NUMBER() over (partition by date order by id) as rowNum
from
x
), b as
(
select
id, date,
ROW_NUMBER() over (partition by id, stat order by date) as rowNum
from
x
)
select min(b.date)
from a
join b on b.id = a.id
having max(a.date) > max(b.date)
What you are looking for is a gaps-and-islands scenario, where you only have islands. In this scenario what defines the start of an island is a change in the stat value within a id, while evaluating the dataset in date order.
The lag window function is used below to compare values across rows, and see if you need to include it in the output.
select b.id
, b.stat
, b.date
from (
select a.id
, a.date
, a.stat
, case lag(a.stat,1,NULL) over (partition by a.id order by a.date asc) when a.stat then 0 else 1 end as include_flag
from x as a
) as b
where b.include_flag = 1
I have table like below,
Txn_Id Txn_Type
___________________
1 101
1 102
1 103
1 104
2 101
2 102
2 104
3 101
3 104
I want rows which has only txn_type 101 and 104. For eg., I should get only Txn_Id "3" for above data.
I tried like below and getting result. Is it possible to have single query to achive this.
Select txn_id from Txn where txn_id in (Select txn_id from Txn where txn_id = 101) and txn_id =104.
Select txn_id from Txn where txn_type in (101,104)
option 2
Select txn_id from Txn where (txn_type = 101 OR txn_type=104)
To get only "3"
Select distinct txn_id from Txn t1 where (txn_type = 101 OR txn_type=104)
and not exists(
select 1 from Txn t2 where t2.txn_type IN (102,103) and t2.txn_id = t1.txn_id
)
Hi As per your above comments you only need the txn_id =3(max)
Please Find the code below.
DECLARE #Table1 TABLE
(txn_id int, Txn_Type int)
;
INSERT INTO #Table1
(txn_id , Txn_Type )
VALUES
(1, 101),
(1, 102),
(1, 103),
(1, 104),
(2, 101),
(2, 102),
(2, 104),
(3, 101),
(3, 104)
;
Select max(txn_id ),Txn_Type
from #Table1 where item in (101,104)
group by Txn_Type
As balaji pointed out, #Ayush solution is not flexible, since will return incorrect results if you, for example, add another pair of records in the table (4,101) and (4,104). IMO, you have to join table to itself for some filtering, something like this:
DECLARE #Table1 TABLE
(txn_id int, Txn_Type int);
INSERT INTO #Table1
(txn_id , Txn_Type )
VALUES
(1, 101),
(1, 102),
(1, 103),
(1, 104),
(2, 101),
(2, 102),
(2, 104),
(3, 101),
(3, 104),
(4, 101),
(4, 104);
select t1.*
from #Table1 t1
inner join (select txn_id, count(*) as total
from #Table1
group by Txn_id
having count(*) < 3
) t2 on t2.txn_id = t1.txn_id
where t1.Txn_Type in (101,104)