Complex SQL Select Query - sql

Table schema:
CREATE TABLE TRANSACTIONDETAILS
(
TransNo CHAR(15),
Serial INT,
Project CHAR(3)
)
Dataset:
+-----------------+--------+---------+
| TransNo | Serial | Project |
+-----------------+--------+---------+
| A00000000000001 | 1 | 100 |
| A00000000000001 | 2 | 101 |
| A00000000000002 | 1 | 100 |
| A00000000000002 | 2 | 101 |
| A00000000000003 | 1 | 100 |
| A00000000000003 | 2 | 200 |
| A00000000000004 | 1 | 200 |
| A00000000000004 | 2 | 100 |
| A00000000000005 | 1 | 101 |
| A00000000000005 | 2 | 100 |
+-----------------+--------+---------+
I want to identify transactions those have same project sets.
Expected output:
+-----------------+--------+---------+---------+
| TransNo | Serial | Project | Flag |
+-----------------+--------+---------+---------+
| A00000000000001 | 1 | 100 | 1 |
| A00000000000001 | 2 | 101 | 1 |
| A00000000000002 | 1 | 100 | 1 |
| A00000000000002 | 2 | 101 | 1 |
| A00000000000005 | 1 | 101 | 1 |
| A00000000000005 | 2 | 100 | 1 |
| A00000000000003 | 1 | 100 | 2 |
| A00000000000003 | 2 | 200 | 2 |
| A00000000000004 | 1 | 200 | 2 |
| A00000000000004 | 2 | 100 | 2 |
+-----------------+--------+---------+---------+
I am using SQL Server 2012 and later.
Thanks.
UPDATE 1: Partially my objective would be achieved if I make following from input dataset.
+-----------------+---------+---------+
| TransNo | Project1| Project2|
+-----------------+---------+---------+
| A00000000000001 | 100 | 101 |
| A00000000000002 | 100 | 101 |
| A00000000000003 | 100 | 200 |
| A00000000000004 | 200 | 100 |
| A00000000000005 | 101 | 100 |
+-----------------+---------+---------+
UPDATE 2:
Data set
+-----------------+--------+---------+
| TransNo | Serial | Project |
+-----------------+--------+---------+
| A00000000000001 | 1 | 100 |
| A00000000000001 | 2 | 101 |
| A00000000000001 | 3 | 200 |
| A00000000000002 | 1 | 100 |
| A00000000000002 | 2 | 101 |
| A00000000000003 | 1 | 100 |
| A00000000000003 | 2 | 200 |
| A00000000000004 | 1 | 200 |
| A00000000000004 | 2 | 100 |
| A00000000000005 | 1 | 101 |
| A00000000000005 | 2 | 100 |
+-----------------+--------+---------+
Output:
+-----------------+--------+---------+---------+
| TransNo | Serial | Project | Flag |
+-----------------+--------+---------+---------+
| A00000000000001 | 1 | 100 | 1 |
| A00000000000001 | 2 | 101 | 1 |
| A00000000000001 | 2 | 200 | 1 |
| A00000000000002 | 1 | 100 | 2 |
| A00000000000002 | 2 | 101 | 2 |
| A00000000000005 | 1 | 101 | 2 |
| A00000000000005 | 2 | 100 | 2 |
| A00000000000003 | 1 | 100 | 3 |
| A00000000000003 | 2 | 200 | 3 |
| A00000000000004 | 1 | 200 | 3 |
| A00000000000004 | 2 | 100 | 3 |
+-----------------+--------+---------+---------+

Try this
;WITH cte
AS (SELECT *,
Concat(Min(Project)OVER(partition BY TransNo), Max(Project)OVER(partition BY TransNo)) AS inter
FROM TRANSACTIONDETAILS)
SELECT TransNo,
Serial,
Project,
Dense_rank()OVER(ORDER BY inter) AS flag
FROM cte
SQL FIDDLE DEMO
Update : For partial result
SELECT TransNo,
Max(CASE WHEN Serial = 1 THEN Project END) AS Project_1,
Max(CASE WHEN Serial = 2 THEN Project END) AS Project_2
FROM TRANSACTIONDETAILS
GROUP BY TransNo

CREATE TABLE #test_trans
([TransNo] varchar(15), [Serial] int, [Project] int)
;
INSERT INTO #test_trans
([TransNo], [Serial], [Project])
VALUES
('A00000000000001', 1, 100),
('A00000000000001', 2, 101),
('A00000000000001', 3, 200),
('A00000000000002', 1, 100),
('A00000000000002', 2, 101),
('A00000000000003', 1, 100),
('A00000000000003', 2, 200),
('A00000000000004', 1, 200),
('A00000000000004', 2, 100),
('A00000000000005', 1, 101),
('A00000000000005', 2, 100)
;
[![;WITH cte
AS (select \[TransNo\],(
Select cast(ST1.\[Project\] as varchar(max)) AS \[text()\]
From #test_trans ST1
where st1.TransNo=st2.TransNo
For XML PATH ('')) as rn,Project,st2.Serial from #test_trans st2)
SELECT TransNo,
Serial,
Project,
Dense_rank()OVER(ORDER BY rn) AS flag
FROM cte][1]][1]

Related

Append Row To Each Group in SQL

Let's say that I have database table:
| id | value | rank |
| -----------|-----------|-------------|
| 303 | D | 3 |
| 404 | A | 1 |
| 505 | B | 1 |
| 505 | D | 4 |
| 202 | B | 1 |
| 505 | A | 5 |
| 303 | N | 2 |
| 101 | A | 1 |
| 505 | A | 7 |
| 202 | A | 6 |
| 202 | N | 3 |
| 505 | N | 3 |
| 202 | A | 4 |
| 505 | A | 2 |
| 202 | N | 5 |
| 303 | A | 1 |
| 505 | N | 6 |
| 202 | A | 2 |
Following:
SELECT *
FROM table_name
GROUP BY id
ORDER BY rank;
I get:
| id | value | rank |
| -----------|-----------|-------------|
| 101 | A | 1 |
| 202 | B | 1 |
| 202 | A | 2 |
| 202 | N | 3 |
| 202 | A | 4 |
| 202 | N | 5 |
| 202 | A | 6 |
| 303 | A | 1 |
| 303 | N | 2 |
| 303 | D | 3 |
| 404 | A | 1 |
| 505 | B | 1 |
| 505 | A | 2 |
| 505 | N | 3 |
| 505 | D | 4 |
| 505 | A | 5 |
| 505 | N | 6 |
| 505 | A | 7 |
However, for each group, I'd like to append an additional row with the value column taken from the id column so that the resulting table looks like:
| id | value | rank |
| -----------|-----------|-------------|
| 101 | A | 1 |
| 101 | 101 | 2 |
| 202 | B | 1 |
| 202 | A | 2 |
| 202 | N | 3 |
| 202 | A | 4 |
| 202 | N | 5 |
| 202 | A | 6 |
| 202 | 202 | 7 |
| 303 | A | 1 |
| 303 | N | 2 |
| 303 | D | 3 |
| 303 | 303 | 4 |
| 404 | A | 1 |
| 404 | 404 | 2 |
| 505 | B | 1 |
| 505 | A | 2 |
| 505 | N | 3 |
| 505 | D | 4 |
| 505 | A | 5 |
| 505 | N | 6 |
| 505 | A | 7 |
| 505 | 505 | 8 |
What is ANSI SQL (or most database agnostic) way to accomplish this?
You don't want a group by in the initial set as you appear to want all the rows back:
select "id", "value", "rank"
from T
union all
select "id", cast("id" as varchar(10)), max("rank") + 1
from T
group by "id"
order by "id", "rank";
And you can do this with grouping sets for the fun of it:
select "id",
grouping("rank"),
case when grouping("rank") = 0 then min("value") else cast("id" as varchar(10)) end as "value",
case when grouping("rank") = 0 then "rank" else max("rank") over (partition by "id") + 1 end as "rank"
from T
group by grouping sets ("id", "rank"), ("id")
order by "id", "rank";
Here's how I would do it:
SELECT id,
value,
rank
FROM table_name
UNION
SELECT id,
id AS value,
max(rank) + 1 AS rank
FROM table_name
GROUP BY id
ORDER BY id, rank;
You seem to want:
select id, value, max(rank) as rank
from t
group by id, value
union all
select id, id, max(rank) + 1
from t
group by id;

SQL How count subset with condition

I have the following table:
+--------+------------+----------------+
| saleId | saleDate | contractId |
+--------+------------+----------------+
| 1 | 01.07.2016 | 1001 |
| 2 | 02.07.2016 | 1001 |
| 3 | 03.07.2016 | 1002 |
| 4 | 04.07.2016 | 1002 |
| 5 | 05.07.2016 | 1001 |
| 6 | 06.07.2016 | 1001 |
+--------+------------+----------------+
I want to count number of previuos sales by contract for each sale (each row)
+--------+------------+------------+------------------------+
| saleId | saleDate | contractId | SalesCountPerContract |
+--------+------------+------------+------------------------+
| 1 | 01.07.2016 | 1001 | 0 |
| 2 | 02.07.2016 | 1001 | 1 |
| 3 | 03.07.2016 | 1002 | 0 |
| 4 | 04.07.2016 | 1002 | 1 |
| 5 | 05.07.2016 | 1001 | 2 |
| 6 | 06.07.2016 | 1001 | 3 |
+--------+------------+------------+------------------------+
select t.*
,row_number() over
(partition by contractId order by saleDate) - 1 as SalesCountPerContract
from mytable t

Avoid repeated values in a join

I have two tables - a header and a matrix/details.
*Header Table* *Matrix / Details Table*
+----+--------+-----+ +----+--------+------+
| ID | Parent | Qty | | ID | Child | Qty |
+----+--------+-----+ +----+--------+------+
| 1 | A | 10 | | 1 | X | 100 |
| 2 | B | 20 | | 1 | Y | 1000 |
| 3 | C | 30 | | 2 | X | 200 |
+----+--------+-----+ | 2 | Y | 2000 |
| 3 | X | 30 |
| 3 | Y | 300 |
| 3 | Z | 3000 |
+----+--------+------+
I'm Joining these two tables based on ID.
I don't want the result to have duplicated values from header table.
I expect a result like following:
*Current Result* *Expected Result*
+----+--------+-----+-------+------+ +----+--------+-----+-------+------+
| ID | Parent | Qty | Child | Qty | | ID | Parent | Qty | Child | Qty |
+----+--------+-----+-------+------+ +----+--------+-----+-------+------+
| 1 | A | 10 | X | 100 | | 1 | A | 10 | X | 100 |
| 1 | A | 10 | Y | 1000 | | | | | Y | 1000 |
| 2 | B | 20 | X | 200 | | 2 | B | 20 | X | 200 |
| 2 | B | 20 | Y | 2000 | | | | | Y | 2000 |
| 3 | C | 30 | X | 30 | | 3 | C | 30 | X | 30 |
| 3 | C | 30 | Y | 300 | | | | | Y | 300 |
| 3 | C | 30 | Z | 3000 | | | | | Z | 3000 |
+----+--------+-----+-------+------+ +----+--------+-----+-------+------+
Is this possible? If not any, alternate solution available?
Thanks in advance...
If you are using SQL Server,Try with the below query.
;WITH CTE_1
AS
(SELECT *,ROW_NUMBER()OVER(PARTITION BY ID,Parent,Quantity ORDER BY ID ) RNO
FROM Header H
JOIN [Matrix / Details] M
ON H.ID=M.ID)
SELECT CASE WHEN RNO=1 THEN CAST(ID as VARCHAR(50)) ELSE '' END ID,
CASE WHEN RNO=1 THEN Parent ELSE '' END Parent,
CASE WHEN RNO=1 THEN cast(Quantity as VARCHAR(50)) ELSE '' END Quantity,
Child,Qty
FROM CTE_1
ORDER BY ID,Parent,Quantity

sum qty with different date SQL Server

I have 2 tables,
table 1 is transaction table
+----------+-----------+---------+------------+-----+
| IDOutlet | IDProduct | TrxType | TrxDate | Qty |
+----------+-----------+---------+------------+-----+
| 101 | ASD11 | 2 | 11/11/2015 | 15 |
| 101 | ASD11 | 3 | 11/14/2015 | -3 |
| 101 | ASD11 | 3 | 11/17/2015 | -6 |
| 101 | ASD11 | 2 | 11/22/2015 | 7 |
| 101 | ASD11 | 3 | 11/26/2015 | -2 |
| 101 | ASD11 | 2 | 12/3/2015 | 1 |
| 101 | ASD11 | 3 | 12/9/2015 | -3 |
| 101 | ASD11 | 3 | 12/11/2015 | -2 |
| 101 | ASD11 | 2 | 12/12/2015 | 5 |
| 101 | FFD34 | 2 | 11/11/2015 | 9 |
| 101 | FFD34 | 3 | 11/14/2015 | -3 |
| 101 | FFD34 | 2 | 11/16/2015 | 3 |
| 101 | FFD34 | 3 | 11/19/2015 | -4 |
| 101 | FFD34 | 3 | 11/23/2015 | -3 |
| 102 | FFD34 | 2 | 11/26/2015 | 2 |
| 102 | FFD34 | 2 | 11/28/2015 | 4 |
| 102 | FFD34 | 3 | 11/29/2015 | -5 |
| 102 | FFD34 | 3 | 12/1/2015 | -1 |
+----------+-----------+---------+------------+-----+
Table 2 is opnametable
+----------+-----------+------------+-----------+
| IDOutlet | IDProduct | OpnameDate | QtyOpname |
+----------+-----------+------------+-----------+
| 101 | ASD11 | 11/20/2015 | 5 |
| 101 | FFD34 | 11/30/2015 | 5 |
| 102 | FFD34 | 11/30/2015 | 1 |
+----------+-----------+------------+-----------+
And I want the result like this
+----------+-----------+------------+---------+
| IDOutlet | IDProduct | OpnameDate | Sum Qty |
+----------+-----------+------------+---------+
| 101 | ASD11 | 11/20/2015 | 6 |
| 101 | FFD34 | 11/20/2015 | 5 |
| 102 | FFD34 | 11/30/2015 | 1 |
+----------+-----------+------------+---------+
You can use a date comparison in your JOIN criteria:
SELECT T2.IDOutlet,T2.IDProduct,T2.OpnameDate,SUM(T1.Qty) AS Sum_Qty
FROM opnametable T2
LEFT JOIN transaction T1
ON T2.IDOUtlet = T1.IDOutlet
AND T2.IDProduct = T1.IDProduct
AND T1.TrxDate <= T2.OpnameDate
GROUP BY T2.IDOutlet,T2.IDProduct,T2.OpnameDate
I'm assuming the dates are stored in an appropriate date datatype, and that you want to include OpnameDate.
Try this
select o.IDOutlet,o.IDProduct,sum(t.Qty) as [Sum Qty]
from opnametable o left outer join transaction1 t on o.IDOutlet=t.IDOutlet
and o.IDProduct=t.IDProduct and t.trxdate<o.OpnameDate
group by o.IDOutlet,o.IDProduct

how to write a query to get multilevel data

I have four tables as below:
tblAccount
Id i sprimary key
+----+-----------------+
| Id | AccName |
+----+-----------------+
| 1 | AccountA |
| 2 | AccountB |
+----+-----------------+
tblLocation
Id is primary key.
+----+---------------+
| Id | LocName |
+----+---------------+
| 1 | LocationA |
| 2 | LocationB |
| 3 | LocationC |
+----+---------------+
tblAccountwiseLocation
Id i sprimary key.LocId and AccId are foreign key.
+----+---------------+---------------+
| Id | LocId | AccId |
+----+---------------+---------------+
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 3 | 1 |
| 4 | 1 | 2 |
| 5 | 2 | 2 |
| 6 | 3 | 2 |
+----+---------------+---------------+
tblRSCMaster
Id i sprimary key.LocId and AccId are foreign key.
+----+---------------+---------------+----------------+------------------+
| Id | LocId | AccId | RSCNo | DateOfAddition |
+----+---------------+---------------+----------------+------------------+
| 1 | 1 | 1 | Acc1_Loc1_1_14 | 15/01/2014 |
| 2 | 2 | 1 | Acc1_Loc2_1_14 | 15/01/2014 |
| 3 | 3 | 1 | Acc1_Loc2_1_14 | 15/01/2014 |
| 4 | 1 | 2 | Acc2_Loc1_1_14 | 15/01/2014 |
| 5 | 2 | 2 | Acc2_Loc2_1_14 | 15/01/2014 |
| 6 | 3 | 2 | Acc2_Loc3_1_14 | 15/01/2014 |
| 7 | 1 | 1 | Acc1_Loc1_2_14 | 15/02/2014 |
| 8 | 2 | 1 | Acc1_Loc2_2_14 | 15/02/2014 |
| 9 | 3 | 1 | Acc1_Loc3_2_14 | 15/02/2014 |
| 10 | 1 | 2 | Acc2_Loc1_2_14 | 15/02/2014 |
| 11 | 2 | 2 | Acc2_Loc2_2_14 | 15/02/2014 |
| 12 | 3 | 2 | Acc2_Loc3_2_14 | 15/02/2014 |
| 13 | 1 | 1 | Acc1_Loc1_3_14 | 15/03/2014 |
| 14 | 2 | 1 | Acc1_Loc2_3_14 | 15/03/2014 |
| 15 | 3 | 1 | Acc1_Loc3_3_14 | 15/03/2014 |
| 16 | 1 | 2 | Acc2_Loc1_3_14 | 15/03/2014 |
| 17 | 2 | 2 | Acc2_Loc2_3_14 | 15/03/2014 |
| 18 | 3 | 2 | Acc2_Loc3_3_14 | 15/03/2014 |
| 19 | 1 | 1 | Acc1_Loc1_4_14 | 15/04/2014 |
| 20 | 2 | 1 | Acc1_Loc2_4_14 | 15/04/2014 |
| 21 | 3 | 1 | Acc1_Loc3_4_14 | 15/04/2014 |
| 22 | 1 | 2 | Acc2_Loc1_4_14 | 15/04/2014 |
| 23 | 2 | 2 | Acc2_Loc2_4_14 | 15/04/2014 |
| 24 | 3 | 2 | Acc2_Loc3_4_14 | 15/04/2014 |
| 25 | 1 | 1 | Acc1_Loc1_5_14 | 15/05/2014 |
| 26 | 2 | 1 | Acc1_Loc2_5_14 | 15/05/2014 |
| 27 | 3 | 1 | Acc1_Loc3_5_14 | 15/05/2014 |
| 28 | 1 | 2 | Acc2_Loc1_5_14 | 15/05/2014 |
| 29 | 2 | 2 | Acc2_Loc2_5_14 | 15/05/2014 |
| 30 | 3 | 2 | Acc2_Loc3_5_14 | 15/05/2014 |
+----+---------------+---------------+----------------+------------------+
Acc1_Loc1_1_14 resembles RSC for LocationA of AccountA for Jan 2014.
I need to get a output as below from tblRSCMaster.
+---------------+---------------+----------------+------------------+
| LocId | AccId | RSCNo | DateOfAddition |
+---------------+---------------+----------------+------------------+
| 1 | 1 | Acc1_Loc1_3_14 | 15/03/2014 |
| 1 | 1 | Acc1_Loc1_4_14 | 15/04/2014 |
| 1 | 1 | Acc1_Loc1_5_14 | 15/05/2014 |
| 2 | 1 | Acc1_Loc2_3_14 | 15/03/2014 |
| 2 | 1 | Acc1_Loc2_4_14 | 15/04/2014 |
| 2 | 1 | Acc1_Loc2_5_14 | 15/05/2014 |
| 3 | 1 | Acc1_Loc3_3_14 | 15/03/2014 |
| 3 | 1 | Acc1_Loc3_4_14 | 15/04/2014 |
| 3 | 1 | Acc1_Loc3_5_14 | 15/05/2014 |
+---------------+---------------+----------------+------------------+
Each account has multiple locations and each location has multiple RSCs.
I need to get last three RSCs for each location for AccountA.
I have tried the below query:
SELECT tblAccountwiseLocation.LocId,tblAccountwiseLocation.AccId,tblRSCMaster.RSCNo,tblRSCMaster.DateOfAddition FROM tblAccountwiseLocation
INNER JOIN tblRSCMaster ON tblAccountwiseLocation.LocId= tblRSCMaster.LocId
where tblRSCMaster.AccId=1
But not getting the proper output.
Please help me out.
Thank you all in advance.
You can wrap the existing query inside a common table expression, and use ROW_NUMBER() to get only the last 3 (by tblRSCMaster.DateOfAddition) entries per tblAccountwiseLocation.LocId.
WITH cte AS (
SELECT tblAccountwiseLocation.LocId,
tblAccountwiseLocation.AccId,
tblRSCMaster.RSCNo,
tblRSCMaster.DateOfAddition,
ROW_NUMBER() OVER (PARTITION BY tblAccountwiseLocation.LocId
ORDER BY tblRSCMaster.DateOfAddition DESC) rn
FROM tblAccountwiseLocation
INNER JOIN tblRSCMaster
ON tblAccountwiseLocation.LocId = tblRSCMaster.LocId
AND tblAccountwiseLocation.AccId = tblRSCMaster.AccId
WHERE tblRSCMaster.AccId=1
)
SELECT LocId, AccId, RSCNo, DateOfAddition
FROM cte
WHERE rn <= 3
ORDER BY LocId, AccId, DateOfAddition
An SQLfiddle to test with.
Is this what you need?
select m.*
from (select m.*, row_number() over (partition by accID
order by DateOfAddition desc) as seqnum
from tblRSCMaster
where m.locid = 1
) m
where seqnum <= 3
order by AccId, DateOfAddition;
I think you need to filter on the locid rather than on the AccId to get what you want.