Append Row To Each Group in SQL - 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;

Related

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

Complex SQL Select Query

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]

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.

Ask about query in sql server

i have table like this:
| ID | id_number | a | b |
| 1 | 1 | 0 | 215 |
| 2 | 2 | 28 | 8952 |
| 3 | 3 | 10 | 2000 |
| 4 | 1 | 0 | 215 |
| 5 | 1 | 0 |10000 |
| 6 | 3 | 10 | 5000 |
| 7 | 2 | 3 |90933 |
I want to sum a*b where id_number is same, what the query to get all value for every id_number? for example the result is like this :
| ID | id_number | result |
| 1 | 1 | 0 |
| 2 | 2 | 523455 |
| 3 | 3 | 70000 |
This is a simple aggregation query:
select id_number, sum(a*b)
from t
group by id_number
I'm not sure what the first column is for.

Union of two tables in a 3rd table with additional flag column using SQL

I have 2 tables:
One is Promotion
| PromoId |Promo Decription|
----------------------
| 101 | abc|
| 102 | pqr|
| 103 | alp|
| 104 | adc|
| 201 | abc|
and the other is PromotionType
| PromoId | PromoType |
----------------------
| 101 | 1 |
| 121 | 2 |
| 188 | 3 |
| 104 | 4 |
| 191 | 4 |
| 102 | 4 |
I want a resultant table
| PromoId | Flag |Promo Decription |PromoType |
----------------------
| 101 | 1 | | 1 |
| 121 | 0 | | 2 |
| 188 | 0 | | 3 |
| 104 | 1 | adc | 4 |
| 191 | 0 | | 4 |
| 102 | 1 | pqr | 4 |
| 103 | 1 | alp | |
| 201 | 0 | abc | |
i.e I want a resultant table , which is the union of two tables .It should not contain duplicate values and the value of flag is set to true for all the values of PromoId's which are common to both tables.
I am using Sql Server as our database.
You can use a FULL OUTER JOIN to perform this:
select
coalesce(p.promoid, t.promoid) promoid,
case when p.promoid = t.promoid then 1 else 0 end flag
from promotion p
full outer join promotiontype t
on p.promoid = t.promoid
order by promoid
See SQL Fiddle with Demo
Result:
| PROMOID | FLAG |
------------------
| 101 | 1 |
| 102 | 1 |
| 103 | 0 |
| 104 | 1 |
| 121 | 0 |
| 188 | 0 |
| 191 | 0 |
| 201 | 0 |
Edit, even with your changes to the data sample the query will still produce the result:
select
coalesce(p.promoid, t.promoid) promoid,
case when p.promoid = t.promoid then 1 else 0 end flag,
isnull(p.[Promo Decription], '') [Promo Decription],
isnull(t.PromoType, null) PromoType
from promotion p
full outer join promotiontype t
on p.promoid = t.promoid
order by
case when PromoType is not null then 0 else 1 end, promotype, promoid
See SQL Fiddle with Demo
Result is:
| PROMOID | FLAG | PROMO DECRIPTION | PROMOTYPE |
-------------------------------------------------
| 101 | 1 | abc | 1 |
| 121 | 0 | | 2 |
| 188 | 0 | | 3 |
| 102 | 1 | pqr | 4 |
| 104 | 1 | adc | 4 |
| 191 | 0 | | 4 |
| 103 | 0 | alp | (null) |
| 201 | 0 | abc | (null) |
You can use the following script:
select a.PromoID,
coalesce((case when b.promoID=a.promoID then '1'
when b.promoID<>a.promoID then '0'
end),'0') flag
from hr.promotion_type a
LEFT OUTER join hr.promotion b
on(a.promoID= b.promoid)
here, the HR is the schema I used, you can use your corresponding schema