SQL Server 2014 - How to get amount for Max Transaction Code - sql

I have three tables Im joining to get results I need
Account Table
Contact Table
FinTrans table for Financial Transactions
With the below code, I get the AccountNumber, FullName, TransCode, TransNo and TransactionDate just fine and without errors. But now, I want to know the Amount for each Transaction Type from that list. But I seem to get multiple rows.
I have added extra code but commented those couple lines out to show what I want to add.
What do I need to add in the Group By section? I've tried adding TransNo, BusinessDay and then TransAmt. But I got multitple rows. I would really just like to show the TransAmt for that TransNo.
My code so far:
SELECT A.AccountNumber
,C.FirstName + ' ' + C.LastName Full_Name
,F.TransCode
--,F.TransAmt
,MAX(TransNo)TransNo
,Cast(Max(BusinessDay) as Date) TransactionDate
FROM FinTrans F
INNER JOIN Account A ON F.AccounttID = A.AccountId
INNER JOIN Contact C ON F.AccountID = C.AccountId
WHERE F.TransCode IN ('Payment', 'Adjustment')
AND F.AccountID IN (12345, 23456, 34567, 45678)
group by AccountNumber, FirstName, LastName, F.TransCode --, TransAmt
ORDER BY AccountNumber,TransactionDate
EDIT:
Adding the table sample data structure
Account Table Fields (A):
---------------------
AccountId
AccountNumber
Contact Table Fields (C):
---------------------
AccountId
FirstName
LastName
FinTrans Table fields (F):
------------------------
AccountId
TransCode
TransNo
TransAmt
BusinessDay
**A.AccountId = C.AccountId = F.AccountId**
FinTrans Table Data (F):
---------------------------------------------------
AccountId|TransCode |TransNo |TransAmt|BusinessDay|
--------------------------------------------------
12345 |Adjustment|A123456 | 545.26| 2018-04-11|
---------------------------------------------------
12345 |Payment |P234567 | 125.14| 2018-04-10|
---------------------------------------------------
12345 |Payment |P234566 | 99.26| 2018-04-08|
---------------------------------------------------
12345 |Adjustment|A356877 | 12.98| 2018-04-06|
---------------------------------------------------
Expected Result:
-------------------------------------------------------------------------
AccountNumber|Full_Name |TransCode | TransAmt|TransNo |BusinessDay|
-------------------------------------------------------------------------
A12345 |John Doe |Adjustment| 545.26|A123456 | 2018-04-11|
-------------------------------------------------------------------------
A12345 |John Doe |Payment | 125.14|P234567 | 2018-04-10|
-------------------------------------------------------------------------

Try this:
SELECT A.AccountNumber ,C.FirstName + ' ' + C.LastName
Full_Name,T.TransCode
,F.TransAmt
,T.TransNo
,T.TransactionDate
FROM (SELECT MAX(TransNo) TransNo,Cast(Max(BusinessDay) as Date)
TransactionDate, AccountId, TransCode FROM FinTrans WHERE TransCode IN
('Payment', 'Adjustment') AND AccountID IN (12345, 23456, 34567,
45678)
group by AccountId, TransCode) T
INNER JOIN FinTrans F ON T.AccountID = F.AccountId and T.TransNo=F.TransNo
INNER JOIN Account A ON T.AccountID = A.AccountId
INNER JOIN Contact C ON T.AccountID = C.AccountId

Have you tried adding the column to the transaction value?
SUM(F.TransAmt)

Look:
SELEC Name, Date, Value FROM x
Return
Jean 19-APR-2018 10
Jean 19-APR-2018 20
But
SELEC Name, Date, SUM(Value) FROM x GROUP BY Name
Will return
Jean 19-APR-2018 30

Related

SQL Server 2014 : how to get records that have rows of different values of only particular column

Our customers have accounts with us for their vehicles. Some customers have multiple vehicles, hence multiple licence plates. Sometimes, some customers have different Insurance Accounts for their vehicles too.
I need a list of accounts that have:
More than 1 licence plate and
More than 1 insurance account ID
I'm using 3 tables:
Account table A for account info
Plate table P for licence plate info
EventLog table E for info when notice letter was sent
Relationship:
A.AccountId = P.AccountId = E.AccountId
My code so far:
SELECT
A.AccountNumber, A.AccountId, A.CurrentBalance,
E.NotificationDt,
P.LicPlateNo, A.RegistrationTypeId, P.InsuranceAccountId
FROM
Account A
INNER JOIN
Plate P ON A.AccountId = P.AccountId
INNER JOIN
EventLog E ON A.AccountId = E.AccountId
WHERE
A.RegistrationTypeId = 3
AND P.EndDate IS NULL
AND A.AccountStatusId = 1
AND A.DelinquencyStatusId = 11
AND E.EventId = 64
AND P.PlateStatusId = 1
ORDER BY
AccountNumber, A.AccountId, P.LicPlateNo
My sample data looks like this:
+---------+---------+---------+-------------+----------+---+--------+
|AccNo | AccId |CurrBal |NotifDt |LicPlateNo|RTI|InsAccId|
+---------+---------+---------+-------------+----------+---+--------+
|21234561 |123456 | 56.79| 2017-01-01|ABC123 | 3|1234ABC |
|21234572 |123457 | 83.25| 2017-01-03|DEF345 | 3|345DEF |
|22345672 |234567 | 104.38| 2017-01-03|GHI345 | 3|567GHI |
|22345672 |234568 | 104.38| 2017-01-03|JKL678 | 3|789MNO |
+---------+---------+---------+-------------+----------+---+--------+
In my sample data, the last two columns are for the same AccountNumber which has different LicencePlateNos and different Insurance AccountIds.
I would like my data to look like this:
+---------+---------+---------+-------------+----------+---+--------+
|22345672 |234567 | 104.38| 2017-01-03|GHI345 | 3|567GHI |
|22345672 |234568 | 104.38| 2017-01-03|JKL678 | 3|789MNO |
+---------+---------+---------+-------------+----------+---+--------+
What would the code be that would give me that data?
I tried using a GROUP BY... HAVING clause but that gives me only the unique AccountNumbers that have multiple licence plates. I would like to show the different licence plate nos and the different InsuranceAccountIDs as well.
An easy way to do this would be to JOIN a result set that would allow you for checking for either or both conditions. Just include the appropriate filter clause:
...
INNER JOIN EventLog E ON A.AccountId = E.AccountId
INNER JOIN
(
SELECT
Plate.AccountId,
LicensePlateCount = COUNT(DISTINCT Plate.LicPlateNo),
InsuranceAccountCount = COUNT(DISTINCT Plate.InsuranceAccountId)
FROM
Plate
WHERE
Plate.EndDate IS NULL
AND Plate.PlateStatusId = 1
GROUP BY
Plate.AccountId
) PlateAccounts ON A.AccountId = PlateAccounts.AccountId
-- 1) More than 1 License Plate:
AND PlateAccounts.LicensePlateCount > 1
-- 2) More than 1 Insurance Account IDs:
AND PlateAccounts.InsuranceAccountCount > 1
WHERE A.RegistrationTypeId = 3
...
A simple way is to use your query as a base and count the values with more than one row:
SELECT a.*
FROM (SELECT A.AccountNumber, A.AccountId, A.CurrentBalance, E.NotificationDt,
P.LicPlateNo, A.RegistrationTypeId, P.InsuranceAccountId,
COUNT(*) OVER (PARTITION BY A.AccountNumber) as cnt
FROM Account A INNER JOIN
Plate P
ON A.AccountId = P.AccountId INNER JOIN
EventLog E
ON A.AccountId = E.AccountId
WHERE A.RegistrationTypeId = 3 AND
P.EndDate IS NULL AND
A.AccountStatusId = 1 AND
A.DelinquencyStatusId = 11 AND
E.EventId = 64 AND
P.PlateStatusId = 1
) a
WHERE cnt > 1;
ORDER BY AccountNumber, AccountId, LicPlateNo;

How to count occurence of IDs and show this amount with name of item with this ID from other table in SQL?

if I have tables
Person: ID_Person, Name
Profession: ID_Prof, Prof_Name, ID_Person
If ID_Person appears multiple times in second table and I want to show all Person names with number of their professions how can I do this?
I know that if I want to count something I can write
SELECT ID_Person, count(*) as c
FROM Profession
GROUP BY ID_Person;
but don't know how to link it with column from other table in order to proper values.
Here is one way (MySQL InnoDB)
Person
+-----------+-------+
| ID_Person | Name |
+-----------+-------+
| 1 | bob |
| 2 | alice |
+-----------+-------+
Profession
+---------+--------------------+-----------+
| ID_Prof | Prof_Name | ID_Person |
+---------+--------------------+-----------+
| 1 | janitor | 1 |
| 2 | cook | 1 |
| 3 | computer scientist | 2 |
| 4 | home maker | 2 |
| 7 | astronaut | 2 |
+---------+--------------------+-----------+
select Name, count(Prof_Name)
from Person left join Profession
on (Person.ID_Person=Profession.ID_Person)
group by Name;
+-------+------------------+
| Name | count(Prof_Name) |
+-------+------------------+
| alice | 3 |
| bob | 2 |
+-------+------------------+
Hope this helps.
To just show those with multiple Profession then you would join the two tables, and aggregate with count() using group by and filter using having():
select pe.ID_Person, pe.Name, count(*) as ProfessionCount
from Person pe
inner join Profession pr
on pe.ID_Person = pr.ID_Person
group by pe.ID_Person, pe.Name
having count(*)>1
If you want to show the professions for those people as well:
select
multi.ID_Person
, multi.Name
, multi.ProfessionCount
, prof.ID_Prof
, prof.Prof_Name
from (
select pe.ID_Person, pe.Name, count(*) as ProfessionCount
from Person pe
inner join Profession pr
on pe.ID_Person = pr.ID_Person
group by pe.ID_Person, pe.Name
having count(*)>1
) multi
inner join Profession prof
on multi.ID_Person = prof.ID_Person
you can probably try something like this below. However, you will have to think about whether or not you need to left join versus inner join. You would want to left join if there is potentially someone who has not had any professions and therefore does not exist in the professions table.
SELECT pe.Name
, Professions = COUNT(pr.Prof_Name)
FROM dbo.Person (NOLOCK) pe
JOIN dbo.Profession (NOLOCK) pr ON pe.ID_Person = pr.ID_Person
GROUP BY pe.Name
You're looking for something like this I believe. The left join will bring in all the data and won't exclude any users.
The join can also be a inner join. Inner join would then only show users that exist in both tables.
LEFT
select x.ID_Person, count(x.ID_Person) as [count] from table1 x
left join table2 y on y.ID_Person= x.ID_Person
where x.ID_Person <> null
group by x.ID_Person
INNER
select x.ID_Person, count(y.ID_Person) from table1 x
inner join table2 y on y.ID_Person= x.ID_Person
group by x.ID_Person
The easiest solution is probably counting in a subquery:
select
id_person,
name,
(select count(*) from profession pr where pr.id_person = p.id_person) as profession_count
from person p;
You can achieve the same with an outer join:
select
p.id_person,
p.name,
coalesce(pr.cnt, 0) as profession_count
from person p
left join (select id_person, count(*) as cnt from profession group by id_person) pr
on pr.id_person = p.id_person;
It's usually a good idea to aggregate before joining. Anyway, this is how to join first and aggregate then:
select
p.id_person,
p.name,
coalesce(count(pr.id_person), 0) as profession_count
from person p
left join profession pr on pr.id_person = p.id_person
group by p.id_person, p.name;
As per standard SQL it would suffice to group by p.id_person, as the name functionally depends on the id (i.e. the id uniquely defines a person, so it's one single name belonging to it). Some DBMS however don't fully comply with the standard here and demand you to either put the name in the group by clause as shown or dummy-aggregate it in the select clause (e.g. max(p.name)) instead.

Select distinct values in same column by group

Using Microsoft Access and having a hard time figuring out how to get the desired results. I have two linked tables with an inner join on [bed] with the following data in both tables. I am looking to select [room number] that has a [gender] mismatch (has more than one distinct value per room number). I have searched stackoverflow and haven't found anything that seems to both group AND select distinct by group [room number].
Table 1
-----------------
Room Number | Bed
101 | 101a
101 | 101b
101 | 101c
102 | 102a
102 | 102b
103 | 103a
103 | 103b
Table 2
-----------------
Bed | Gender
101a | Male
101b | Male
101c | Female
102a | Male
102b | Male
103a | Female
103b | Undefined
With this data set, I would expect it to return Room 101 and 103 with the associated genders.
SQL Query
SELECT ROOM_NO
FROM RMGT_T_ROOMS INNER JOIN RMGT_T_ROOM_CONFIGS ON RMGT_T_ROOMS.PK_BED_SPACE = RMGT_T_ROOM_CONFIGS.CK_BED_SPACE
GROUP BY RMGT_T_ROOMS.FK_ROOM_NO
HAVING COUNT(DISTINCT GENDER) > 1
You could join that table on the bed's id and count the distinct number of genders:
SELECT room_number
FROM t1
JOIN t2 ON t1.bed = t2.bed
GROUP BY room_number
HAVING COUNT(DISTINCT gender) > 1
How about inner join and group by?
select t1.room
from t1 inner join
t2
on t1.bed = t2.bed
group by t1.room
having min(gender) <> max(gender);
If you know there are two genders only, you can add min(gender) and max(gender) to the select.
Another method uses exists:
select t1.room, t2.gender
from t1 inner join
t2
on t1.bed = t2.bed
where exists (select 1
from t1 as t11 inner join
t2 as t22
on t11.bed = t22.bed
where t22.gender <> t2.gender
);

SQL Add hours for employees

I have a table for employees signing in and out. They have a date and time field for in and out and an PersonID number that links to the employees name etc.
I need to work out the difference between the 2 dates and times then add them all together for each employee.
select a.*,
b.timein,
b.timeout,
datediff(mi,b.timein,b.timeout) as total_mins
from tbl_people a
left join tbl_register b on a.id=b.personid
Output:
+----+-----------+----------+-------------------------+-------------------------+------------+
| ID | FirstName | LastName | TimeIn | TimeOut | Total_Mins |
+----+-----------+----------+-------------------------+-------------------------+------------+
| 1 | David | Test | 2015-05-12 12:11:00.000 | 2015-05-12 12:13:00.000 | 2 |
| 2 | David | Test | 2015-05-12 12:15:00.000 | 2015-05-12 12:18:00.000 | 3 |
+----+-----------+----------+-------------------------+-------------------------+------------+
This is what im currently getting. I would like it to show one record for each person with the total amount of minutes worked.
Thanks in anticipation!
Basically you have at least 2 options:
Option 1 - Use DISTINCT and SUM with OVER clause:
SELECT DISTINCT a.*,
SUM(DATEDIFF(mi, b.timein, b.timeout)) OVER(PARTITION BY a.id) AS total_mins
FROM tbl_people a
LEFT JOIN tbl_register b ON a.id=b.personid
Option 2 - Use a derived table for the GROUP BY part:
SELECT a.*,
total_mins
from tbl_people a
left join (
SELECT personid,
SUM(DATEDIFF(mi, timein, timeout) AS total_mins
FROM tbl_register
GROUP BY personid
) b ON a.id=b.personid
select
ppl.FirstName + ' ' + ppl.LastName as 'Person',
sum( datediff(mi, reg.timein, reg.timeout)) as 'total_mins'
from
tbl_people ppl
left join tbl_register reg on ppl.id = reg.personid
group by
ppl.FirstName + ' ' + ppl.LastName

How to query count(customer), count(order) pair

Assuming we have tables
Order(customerId,orderDetails...)
Customer(Id, customerDetails...)
What is the easiest way to query Count(customer)xCount(order) pair?
example
Customer:
ID | Name
---------
1 | Bob
2 | Ann
Order
CustomerId | Address
--------------------
1 | Block1
1 | Block2
1 | Block1
2 | Home Address
Want to get
CustomerCount | OrderCount
--------------------------
1 | 3
1 | 1
How about
SELECT DISTINCT COUNT(DISTINCT c.ID) AS CustomerCount
, COUNT(*) AS OrderCount
FROM Customer AS c
INNER JOIN [Order] AS o ON o.customerID = c.ID
GROUP BY
c.ID
SQL Fiddle
Edit
Coming to think of it, the previous statement can be reduced to this
SELECT DISTINCT 1 AS CustomerCount
, COUNT(*) AS OrderCount
FROM Customer AS c
INNER JOIN [Order] AS o ON o.customerID = c.ID
GROUP BY
c.ID
but that gives me a nagging feeling that my initial statement is wrong to begin with
select order.id, count(.CustomerId )
from Order order left join Customer customer on order.id = customer.CustomerId
group by order.id