Filter the same payments by different persons - SQL - sql

can you please help me with the following problem :
i have a table Payments :
Id ROLE Payment
1 A 100
1 A 100
1 R 50
1 R 50
1 R 50
2 A 100
2 A 100
2 R 50
2 R 50
2 R 100
Now the ID is one cash loan ( so we have two of them). Role A/R means there are two ppls paying that loan (A-one guy, R- second guy).
Now i need to SUM all the payments for specific ID which is still ok, BUT when the payment for role A is same as for role R i want to count for SUM just one of them..
I was trying to solve it with inner join on the same table where id and payment is the same and role is different but still have the problem that when i have this:
2 A 100
2 A 100
2 R 100
the result is not 100 but 200 ( i will count (A100,R100) = 100 , A100 = 100)
Thank you for any help
UPDATE:
#Giorgi Nakeuri - for given example, the result should looke like :
ID Payment
1 350
2 300
UPDATE : for better understanding : In the given example(for id 2)i have 100payment for R and A so we count it just once = 100, then i have one 100payment just for A = 100, and two payments 50 just for R = 100, 100+100+100 = 300 payment for id 2

Try:
DECLARE #t TABLE ( ID INT, R CHAR(1), P INT )
INSERT INTO #t
VALUES ( 1, 'A', 100 ),
( 1, 'A', 100 ),
( 1, 'R', 50 ),
( 1, 'R', 50 ),
( 1, 'R', 50 ),
( 2, 'A', 100 ),
( 2, 'A', 100 ),
( 2, 'R', 50 ),
( 2, 'R', 50 ),
( 2, 'R', 100 );
WITH cte
AS ( SELECT id ,
P ,
SUM(CASE WHEN R = 'A' THEN 1 ELSE 0 END) cd1 ,
SUM(CASE WHEN R = 'R' THEN 1 ELSE 0 END) cd2
FROM #t
GROUP BY id ,
P
)
SELECT ID ,
SUM(P * CASE WHEN cd1 > cd2 THEN cd1 ELSE cd2 END) AS Sum
FROM cte
GROUP BY ID
Output:
ID Sum
1 350
2 300

Have you try to distinct the query?
SELECT distinct yourField from yourTable

Related

pivot multi column and mutil row

this is my origin data:
ID New LikeNew Old Warehouse
1 10 100 20 LA
1 12 130 40 CA
2 90 200 10 LA
2 103 230 30 CA
i want to get the following format:
ID LA_new LA_likeNew LA_Old CA_New CA_LikeNew CA_Old
1 10 100 20 12 130 40
2 90 200 10 103 230 30
I can only pivot on each column but can not do on all 3 columns(New, LikeNew, Old) , so how can I do that?
You can accomplish this by using conditional logic to create your fields and grouping:
select id,
max(case when warehouse = 'LA' then new else null end) LA_New,
max(case when warehouse = 'LA' then likenew else null end) LA_likeNew,
max(case when warehouse = 'LA' then old else null end) LA_Old,
max(case when warehouse = 'CA' then new else null end) CA_New,
max(case when warehouse = 'CA' then likenew else null end) CA_likeNew,
max(case when warehouse = 'CA' then old else null end) CA_Old
from yourtable
group by id
Alternatively, you can use WITH Common Table Expression
DECLARE #T AS TABLE
(
id INT ,
New INT ,
likeNew INT ,
old INT ,
Warehouse VARCHAR(50)
)
INSERT INTO #T
( id, New, likeNew, old, Warehouse )
VALUES ( 1, 10, 100, 20, 'LA' ),
( 1, 12, 130, 40, 'CA' ),
( 2, 90, 200, 10, 'LA' ),
( 2, 103, 230, 30, 'CA' );
WITH cte
AS ( SELECT *
FROM #T
WHERE Warehouse = 'LA'
),
cte1
AS ( SELECT *
FROM #T
WHERE Warehouse = 'CA'
)
SELECT a.id ,
a.new [LA_New] ,
a.likeNew [LA_LikeNew] ,
a.Old [LA_Old] ,
b.new [CA_New] ,
b.likeNew [CA_LikeNew] ,
b.Old [CA_Old]
FROM cte A
JOIN cte1 B ON a.id = b.id
Result:
id LA_New LA_LikeNew LA_Old CA_New CA_LikeNew CA_Old
----------- ----------- ----------- ----------- ----------- ----------- -----------
1 10 100 20 12 130 40
2 90 200 10 103 230 30

SQL Rank() function excluding rows

Consider I have the following table.
ID value
1 100
2 200
3 200
5 250
6 1
I have the following query which gives the result as follows. I want to exclude the value 200 from rank function, but still that row has to be returned.
SELECT
CASE WHEN Value = 200 THEN 0
ELSE DENSE_RANK() OVER ( ORDER BY VALUE DESC)
END AS RANK,
ID,
VALUE
FROM #table
RANK ID VALUE
1 5 250
0 2 200
0 3 200
4 1 100
5 6 1
But I want the result as follows. How to achieve it?
RANK ID VALUE
1 5 250
0 2 200
0 3 200
2 1 100
3 6 1
If VAL column is not nullable, taking into account NULL is the last value in ORDER BY .. DESC
select *, dense_rank() over (order by nullif(val,200) desc) * case val when 200 then 0 else 1 end
from myTable
order by val desc;
There is no way to exclude Val in Dense Rank currently ,unless you filter in where clause..that is the reason ,you get below result
RANK ID VALUE
1 5 250
0 2 200
0 3 200
4 1 100
5 6 1
You will need to filter once and then do a union all
;with cte(id,val)
as
(
select 1, 100 union all
select 2, 200 union all
select 3, 200 union all
select 5, 250 union all
select 6, 1 )
select *, dense_rank() over (order by val desc)
from cte
where val<>200
union all
select 0,id,val from cte where val=200
You could split the ranking in to separate queries for the values you want to include/exclude from the ranking and UNION ALL the results like so:
Standalone executable example:
CREATE TABLE #temp ( [ID] INT, [value] INT );
INSERT INTO #temp
( [ID], [value] )
VALUES ( 1, 100 ),
( 2, 200 ),
( 3, 200 ),
( 5, 250 ),
( 6, 1 );
SELECT *
FROM ( SELECT 0 RANK ,
ID ,
value
FROM #temp
WHERE value = 200 -- set rank to 0 for value = 200
UNION ALL
SELECT DENSE_RANK() OVER ( ORDER BY value DESC ) AS RANK ,
ID ,
value
FROM #temp
WHERE value != 200 -- perform ranking on records != 200
) t
ORDER BY value DESC ,
t.ID
DROP TABLE #temp
Produces:
RANK ID value
1 5 250
0 2 200
0 3 200
2 1 100
3 6 1
You can modify the ordering at the end of the statement if required, I set it to produce your desired results.
You can also try this, too:
SELECT ISNULL(R, 0) AS Rank ,t.id ,t.value
FROM tbl1 AS t
LEFT JOIN ( SELECT id ,DENSE_RANK() OVER ( ORDER BY value DESC ) AS R
FROM dbo.tbl1 WHERE value <> 200
) AS K
ON t.id = K.id
ORDER BY t.value DESC
The solution in the original question was actually pretty close. Just adding a partition clause to the dense_rank can do the trick.
SELECT CASE
WHEN VALUE = 200 THEN 0
ELSE DENSE_RANK() OVER(
PARTITION BY CASE WHEN VALUE = 200 THEN 0 ELSE 1 END
ORDER BY VALUE DESC
)
END AS RANK
,ID
,VALUE
FROM #table
ORDER BY VALUE DESC;
The 'partition by' creates separate groups for the dense_rank such that the order is performed on these groups individually. This essentially means you create two ranks at the same time, one for the group without the 200 value and one for the group with only the 200 value. The latter one to be set to 0 in the 'case when'.
Standalone executable example:
DECLARE #table TABLE
(
ID INT NOT NULL PRIMARY KEY
,VALUE INT NULL
)
INSERT INTO #table
(
ID
,VALUE
)
SELECT 1, 100
UNION SELECT 2, 200
UNION SELECT 3, 200
UNION SELECT 5, 250
UNION SELECT 6, 1;
SELECT CASE
WHEN VALUE = 200 THEN 0
ELSE DENSE_RANK() OVER(
PARTITION BY CASE WHEN VALUE = 200 THEN 0 ELSE 1 END
ORDER BY VALUE DESC
)
END AS RANK
,ID
,VALUE
FROM #table
ORDER BY VALUE DESC;
RANK ID VALUE
1 5 250
0 2 200
0 3 200
2 1 100
3 6 1

SQL Pivot Query for pivot values

iItemKey Qty FreeQty Unit TaxCatKey TaxVal
7 1 1 1 1 4.00
7 1 1 1 1 1.00
I need output as
iItemKey Qty FreeQty Unit TaxCatKey VAT A.VAt
7 1 1 1 1 4.00 1.00
Here is my query. But i get VAT and A.Vat values as Null
WITH T
AS (SELECT T_ItemRequestSub.iItemKey, T_ItemRequestSub.Qty, T_ItemRequestSub.FreeQty, T_ItemRequestSub.Unit, T_ItemRequestSub.TaxType,
M_Mt_TaxCategorySub.iTaxCatKey , M_Mt_TaxCategorySub.iTaxVal
FROM T_ItemRequestSub INNER JOIN
T_ItemRequest ON T_ItemRequestSub.iReqKey = T_ItemRequest.iKey INNER JOIN
M_Mt_TaxCategory ON T_ItemRequestSub.TaxType = M_Mt_TaxCategory.iKey INNER JOIN
M_Mt_TaxCategorySub ON M_Mt_TaxCategory.iKey = M_Mt_TaxCategorySub.iTaxCatKey where T_ItemRequestSub.iKey = 2)
SELECT *
FROM T PIVOT ( sum (iTaxVal) FOR TaxType IN (
[Vat],
[A.Vat]
) ) AS pvt
Please Help
The only distinct value is TaxVal so I suspect you are trying to do following:
WITH Src AS --Your source table
(
SELECT * FROM (VALUES
(7, 1, 1, 1, 1, 4.00),
(7, 1, 1, 1, 1, 1.00)
)T(iItemKey, Qty, FreeQty, Unit, TaxCatKey, TaxVal)
)
SELECT * FROM
(
SELECT iItemKey, Qty, FreeQty, Unit, TaxCatKey, TaxVal, CASE WHEN TaxVal = 1.00 THEN 'VAT' ELSE 'A.VAT' END Col
FROM Src
) T
PIVOT (MAX(TaxVal) FOR Col IN ([VAT], [A.VAT])) P

SQL: first row of group by after join and order

Assuming we got two below tables:
TravelTimes
OriginId DestinationId TotalJourneyTime
1 1 10
1 2 20
2 2 30
2 3 40
1 3 50
Destinations
DestinationId Name
1 Destination 1
2 Destination 2
3 Destination 3
How do I find the quickest journey between each origin and destination?
I want to join TravelTimes with Destinations by DestinationId and then group them by OriginId and sort each group by TotalJourneyTime and select the first row of each group.
I did try joining and grouping, but it seems group by is not the solution for my case as I don't have any aggregation column in the output.
Expected output
OriginId DestinationId DestinationName TotalJourneyTime
1 1 Destination 1 10
2 3 Destination 3 40
Use a RANK to rank each journey partitioned by the origin and destination and ordered by the travel time
WITH RankedTravelTimes
AS
(
select originid,
destinationId,
totaljourneytime,
rank() over (partition by originid,destinationid order by totaljourneytime ) as r
from traveltimes
)
SELECT rtt.*, d.name
FROM RankedTravelTimes rtt
INNER JOIN Destinations d
ON rtt.destinationId = d.id
WHERE rtt.r=1
The above will include both the journey from 1-2 and 2-2 as separate. If you're only interested in the destination you can remove originId out of the partition.
Not sure I see the problem here with just joining and grouping the data with a MIN on the journey time:
CREATE TABLE #Traveltimes
(
[OriginId] INT ,
[DestinationId] INT ,
[TotalJourneyTime] INT
);
INSERT INTO #Traveltimes
( [OriginId], [DestinationId], [TotalJourneyTime] )
VALUES ( 1, 1, 10 ),
( 1, 2, 20 ),
( 2, 2, 30 ),
( 2, 3, 40 ),
( 2, 3, 50 );
CREATE TABLE #Destinations
(
[DestinationId] INT ,
[Name] VARCHAR(13)
);
INSERT INTO #Destinations
( [DestinationId], [Name] )
VALUES ( 1, 'Destination 1' ),
( 2, 'Destination 2' ),
( 3, 'Destination 3' );
SELECT d.DestinationId ,
d.Name ,
tt.OriginId ,
MIN(tt.TotalJourneyTime) MinTime
FROM #Destinations d
INNER JOIN #Traveltimes tt ON tt.DestinationId = d.DestinationId
GROUP BY tt.OriginId ,
d.DestinationId ,
d.Name
DROP TABLE #Destinations
DROP TABLE #Traveltimes
Gives you:
DestinationId Name OriginId MinTime
1 Destination 1 1 10
2 Destination 2 1 20
2 Destination 2 2 30
3 Destination 3 2 40
Note: why do you travel from destination 1 to itself?
I think you want the following:
;with cte as(select *, row_number() over(partition by DestinationId order by TotalJourneyTime) rn
from TravelTimes)
select * from cte c
join Destinations d on c.DestinationId = d.DestinationId
where c.rn = 1

Complex Query where record is max in set

I have some data that looks like this
CandidateCategory
candidateCategoryId
candidateId
categoryId
I want to return all records where a specific categoryId is the most recent entry, this max(candidateCategoryId)
So if a candidate has 5 categories I want to get that record for say category 23 but only if that is the most recent category added, ie candidateCategoryId is higher than all others for that category.
Using MS SQL 2012
Sample data in format
candidateCategoryId candidateId categoryId
100 1 10
101 1 11
102 1 50
103 1 23
104 1 40
no result, 23 isn't the max candidateCategoryId
candidateCategoryId candidateId categoryId
200 2 20
201 2 31
202 2 12
203 2 23
return result, 23 is the max candidateCategoryId for this candidate.
Try getting the max CandidateCategoryID per CandidateID First, then re-join back
select
yd2.*
from
( select yd.candidateID,
max( yd.candidateCategoryId ) as maxCandCatID
from YourData yd
group by yd.candidateID ) MaxPerID
JOIN YourData yd2
on MaxPerID.candidateID = yd2.candidateID
AND MaxPerID.maxCandCatID = yd2.CandidateCategoryID
AND yd2.categoryID = 23
So, from your sample data, the inner prequery "MaxPerID" will generate two rows...
CandidateID MaxCandCatID (and ultimately corresponds to category ID)
1 104 40
2 203 23
Then, re-joining back to your original table on these two inclusive of your AND CategoryID = 23 will only return the second CandidateID entry
And to help clarify to others who posted answers, the person does not appear to want the highest category ID, but if you look at them, they are sequentially added -- like an auto-incrementing number for the CandidateCategoryID. So, they want the most recent entry for a given candidate (hence candidates 1 & 2)... and if the last entry made was that of category = 23, they want THAT one.
select *
from (select t.*
from tbl t
join (select candidateid,
categoryid,
max(candidatecategoryid) as lastid
from tbl
group by candidateid, categoryid) v
on t.candidateid = v.candidateid
and t.categoryid = v.lastid) x
where categoryid = 23
This is a basic "greatest-of-n" problem. You can solve it with not exists, among other ways:
select t.*
from somedata t
where not exists (select 1
from somedata t2
where t2.categoryId = t.categoryId and
t2.candidateCategoryId > t.candidateCategoryId
);
EDIT:
If you only want categories where the max is 23, then add another condition:
select t.*
from somedata t
where not exists (select 1
from somedata t2
where t2.categoryId = t.categoryId and
t2.candidateCategoryId > t.candidateCategoryId
) and
t.categoryId = 23
Another way to skin this cat. Using your sample data, we can create an inline table for testing and get
DECLARE #candidates TABLE (CandidateCategoryId int,CandidateId int,CategoryId int)
INSERT INTO #candidates
SELECT 100, 1, 10
UNION
SELECT 101, 1, 11
UNION
SELECT 102, 1, 50
UNION
SELECT 103, 1, 23
UNION
SELECT 104, 1, 40
UNION
SELECT 200, 2, 20
UNION
SELECT 201, 2, 31
UNION
SELECT 202, 2, 12
UNION
SELECT 203, 2, 23
SELECT * FROM #candidates c
JOIN
(
SELECT CandidateId,MAX(CategoryId) AS CategoryId FROM #candidates
GROUP BY CandidateId
) tmp
ON c.CandidateId = tmp.CandidateId
AND c.CategoryId = tmp.CategoryId
And get results that look like
CandidateCategoryId | CandidateId | CategoryId
----------------------------------------------
201 | 2 | 31
102 | 1 | 50
I came up with this
select candidateCategoryId
from candidateCategory
where candidateCategoryId in (
select max(candidateCategoryId)
from candidateCategory
group by candidateId )
and categoryId = 23
select *
from yourtable
where candidateCategoryId = (select max(candidateCategoryId) from yourtable)
declare #categoryId int
select #categoryId = 23
with cte as
(
select candidateCategoryId, candidateId, categoryId,
rn = row_number() over (partition by candidateId order by candidateCategoryId desc)
from yourtable
)
select *
from cte c
where exists
(
select *
from cte x
where x.candidateId = c.candidateId
and x.rn = 1
and x.categoryId = #categoryId
)