To select data from multiple records in SQL Server having a common ID - sql

I need to select/concat data from 2 tables in SQL Server I'm using Left Join, but the data is returned as multiple records.
Below are the sample tables
Table1
Id Name Age
1 Sk 20
2 Rb 30
Table2
ID Bike Price Table1Id
1 RX 200 1
2 CD 250 1
3 FZ 300 1
4 R1 400 2
The desired output is
ID Name Age Bike1 Price1 Bike2 Price2 Bike3 Price3
1 Sk 20 RX 200 CD 250 FZ 300
2 Rb 30 R1 400 NULL NULL NULL NULL
A sample format of the query I'm using
SELECT A.ID, A.Name, B.Bike, B.Price FROM Table1 A LEFT JOIN Table2 B ON
A.id = B.Table1Id order by A.id
The output I'm getting from the above query is
ID Name Age Bike Price
1 Sk 20 RX 200
1 Sk 20 CD 250
1 Sk 20 FZ 300
2 Rb 30 R1 400
I need the data as one record for a particular ID and not multiple records (As seen in the desired output). Tired using offset, but offset will return only limited result not the entire records.
Any suggestions on how this can be achieved?

If you know the maximum number of bikes per person, you can use conditional aggregation:
SELECT ID, Name,
MAX(CASE WHEN seqnm = 1 THEN Bike END) as bike_1,
MAX(CASE WHEN seqnm = 1 THEN Price END) as price_1,
MAX(CASE WHEN seqnm = 2 THEN Bike END) as bike_2,
MAX(CASE WHEN seqnm = 2 THEN Price END) as price_2,
MAX(CASE WHEN seqnm = 3 THEN Bike END) as bike_3,
MAX(CASE WHEN seqnm = 3 THEN Price END) as price_3
FROM (SELECT A.ID, A.Name, B.Bike, B.Price,
ROW_NUMBER() OVER (PARTITION BY A.id ORDER BY B.Price) as seqnum
FROM Table1 A LEFT JOIN
Table2 B
ON A.id = B.Table1Id
) ab
GROUP BY ID, Name,
ORDER BY id

Related

How to select data with group by and subquery calculations?

I have two tables:
list_table:
id
name
1
a
2
b
3
c
vt_table:
id
list_id
amount
direction_id
1
1
20
1
2
1
12
2
3
1
15
1
4
2
23
1
5
1
20
1
6
1
20
2
7
1
18
1
I need this result:
amount (dir_id = 1 - dir_id = 2), list_id
amount
list_id
41
1
23
2
0
3
Amount is sum of all amount fields in table vt_table where direction_id = 1 minus sum of all amount fileds in table vt_table where direction_id = 2
And I need group this calculations by list_id, and if table have no rows with list_id 3, as example, amount must be 0.
I'm trying to do it with this query:
SELECT vt.list_id
, ((SELECT COALESCE(SUM(vt.amount), 0)
FROM table_name vt
WHERE vt.direction_id = 1)
-
(SELECT COALESCE(SUM(vt.amount), 0)
FROM table_name vt
WHERE direction_id = 2)) AS result
FROM table_name vt
GROUP BY vt.list_id
But I don't know how to group it correctly and make it so that if there were no entries for some list_id, then the amount was 0 for this list_id.
I use PostgreSQL 12.
Here the examples
You can try to use OUTER JOIN with condition aggregate function with COALESCE fucntion.
Query 1:
SELECT l.id,
SUM(COALESCE(CASE WHEN vt.direction_id = 1 THEN vt.amount END,0)) -
SUM(COALESCE(CASE WHEN vt.direction_id = 2 THEN vt.amount END,0)) AS result
FROM table_name vt
RIGHT JOIN list l ON vt.list_id = l.id
GROUP BY l.id
ORDER BY l.id
Results:
| id | result |
|----|--------|
| 1 | 41 |
| 2 | 23 |
| 3 | 0 |
Try something like this, as a start:
SELECT vt.list_id
, COALESCE(SUM(CASE WHEN direction_id = 1 THEN amount END), 0)
- COALESCE(SUM(CASE WHEN direction_id = 2 THEN amount END), 0) AS result
FROM table_name vt
GROUP BY vt.list_id
;
Result using your fiddle:
list_id
result
1
41
2
23
This just misses the cases where there are no vt rows for some list.
Use an outer join to address those cases.
SELECT SUM(CASE WHEN vt.direction_id = 1 THEN vt.amount ELSE 0 END) - SUM(CASE WHEN vt.direction_id = 2 THEN vt.amount ELSE 0 END) as amount,
lt.id as list_id
FROM list_table lt
LEFT OUTER JOIN vt_table vt
ON lt.id = vt.list_id
GROUP BY lt.id
ORDER BY lt.id

SQL Select Distinct Records From Two Tables

I am trying to write a SQL statement that will return a set of Distinct set of CompanyNames from a table based on the most recent SaleDate withing a specified date range from another table.
T01 = Account
T02 = TransHeader
The fields of importance are:
T01.ID, T01.CompanyName
T02.AccountID, T02.SaleDate
T01.ID = T02.AccountID
What I want to return is the Max SaleDate for each CompanyName without any duplicate CompanyNames and only the Max(SaleDate) as LastSale. I will be using a Where Clause to limit the SaleDate range.
I tried the following but it returns all the records for all SalesDates in the range. This results in the same company being listed multiple times.
Current MS-SQL Query
SELECT T01.CompanyName, T02.LastSale
FROM
(SELECT DISTINCT ID, IsActive, ClassTypeID, CompanyName FROM Account) T01
FULL OUTER JOIN
(SELECT DISTINCT AccountID, TransactionType, MAX(SaleDate) LastSale FROM TransHeader group by AccountID, TransactionType, SaleDate) T02
ON T01.ID = T02.AccountID
WHERE ( ( T01.IsActive = 1 )AND
( (Select Max(SaleDate)From TransHeader Where AccountID = T01.ID AND TransactionType in (1,6) AND SaleDate is NOT NULL)
BETWEEN '01/01/2016' AND '12/31/2018 23:59:00' AND (Select Max(SaleDate)From TransHeader Where AccountID = T01.ID AND TransactionType in (1,6) AND SaleDate is NOT NULL) IS NOT NULL
)
)
ORDER BY T01.CompanyName
I thought the FULL OUTER JOIN was the ticket but it did not work and I am stuck.
Sample data Account Table (T01)
ID CompanyName IsActive ClassTypeID
1 ABC123 1 1
2 CDE456 1 1
3 EFG789 1 1
4 Test123 0 1
5 Test456 1 1
6 Test789 0 1
Sample data Transheader table (T02)
AccountID TransactionType SaleDate
1 1 02/03/2012
2 1 03/04/2013
3 1 04/05/2014
4 1 05/06/2014
5 1 06/07/2014
6 1 07/08/2015
1 1 08/09/2016
1 1 01/15/2016
2 1 03/20/2017
2 1 03/21/2017
3 1 03/04/2017
3 1 04/05/2018
3 1 05/27/2018
4 1 06/01/2018
5 1 07/08/2018
5 1 08/01/2018
5 1 10/11/2018
6 1 11/30/2018
Desired Results
CompanyName LastSale (Notes note returned in the result)
ABC123 01/15/2016 (Max(SaleDate) LastSale for ID=1)
CDE456 03/21/2017 (Max(SaleDate) LastSale for ID=2)
EFG789 05/27/2018 (Max(SaleDate) LastSale for ID=3)
Testing456 10/11/2018 (Max(SaleDate) LastSale for ID=5)
ID=4 & ID=6 are note returned because IsActive = 0 for these records.
One option is to select the maximum date in the select clause.
select
a.*,
(
select max(th.saledate)
from transheader th
where th.accountid = a.id
and th.saledate >= '2016-01-01'
and th.saledate < '2019-01-01'
) as max_date
from account a
where a.isactive = 1
order by a.id;
If you only want to show transaction headers with sales dates in the given date range, then you can just inner join the maximum dates with the accounts. In order to do so, you must group your date aggregation per account:
select a.*, th.max_date
from account a
join
(
select accountid, max(saledate) as max_date
from transheader
and saledate >= '2016-01-01'
and saledate < '2019-01-01'
group by accountid
) th on th.accountid = a.id
where a.isactive = 1
order by a.id;
select CompanyName,MAX(SaleDate) SaleDate from Account a
inner join Transheader b on a.id = b.accountid
group by CompanyName
order by 1

Inner join without duplicate values from the left table?

I'm working with SQL Server, and I have 2 tables: Invoice and InvoiceService.
Invoice:
InvoiceID InvoiceDate InvoicePrice InvoicePaidAmount PatientID
----------------------------------------------------------------------------
1 01-01-2016 50 30 1
2 01-02-2016 100 100 2
InvoiceService:
ID InvoiceID ServiceName ServicePrice
-------------------------------------------------
1 1 Dermato 20
2 1 ophthalmo 30
3 2 General 100
My query:
select
ServiceName, ServicePrice, InvoiceID, InvoicePrice,
InvoicePaidAmount, PatientID
from
InvoiceService
inner join
Invoice on Invoice.InvoiceID = InvoiceService.InvoiceID
Result:
ServiceName ServicePrice InvoiceID InvoicePrice InvoicePaidAmount PatientID
Dermato 20 1 50 30 1
ophthalmo 30 1 50 30 1
General 100 2 100 100 2
I need to get non duplicate values from the left table :
when an invoice has more than 1 service I want that the invoice price and InvoicePaidAmount don't be repeated like this example:
ServiceName ServicePrice InvoiceID InvoicePrice InvoicePaidAmount PatientID
Dermato 20 1 50 30 1
ophthalmo 30 1 0 0 1
General 100 2 100 100 2
If I understand correctly, you want one invoice service to "really" match.
select s.ServiceName, s.ServicePrice, i.InvoiceID,
(case when seqnum = 1 then i.InvoicePrice else 0 end) as InvoicePrice,
(case when seqnum = 1 then i.InvoicePaidAmount else 0 end) as InvoicePaidAmount,
i.PatientID
from Invoice i join
(select s.*,
row_number() over (partition by s.InvoiceID order by s.id) as seqnum
from InvoiceService s
) s
on i.InvoiceID = s.InvoiceID

How to merge rows in select query result set

My result of is like this
date acc cr date acc dr
---------------------------------------------------
null null 0 13/3/12 A 1300
null null 0 13/3/12 c 1200
null null 0 13/3/12 D 1100
13/3/12 A 1000 null null 0
18/3/12 E 2000 null null 0
19/3/12 F 3000 null null 0
31/3/12 G 3000 null null 0
this result i got from following query bu joining 2 tables to get cash book
select(case when mli.voucher_type = 1 THEN tav.voucher_date end)pdate,
(case when mli.voucher_type = 1 THEN mli.description end) acc,
(case when tav.voucher_type_id = 1 then sum(tvl.amount) else 0 end) cr,
(case when mli.voucher_type = 2 THEN tav.voucher_dateend) rdate,
(case when mli.voucher_type = 2 THEN mli.description end) acc,
(case when tav.voucher_type_id = 2 then sum(tvl.amount) else 0 end) dr
from t_acc_voucher tav
join t_voucher_ledger tvl on tvl.voucher_id = tav.voucher_id
join m_ledger_index mli on mli.ledger_index_id = tvl.ledger_index_id
group by mli.description, mli.voucher_type, tav.voucher_type_id,tav.voucher_date
I want result like this
date acc cr date acc dr
---------------------------------------------------
13/3/12 A 1000 13/3/12 A 1300
18/3/12 E 2000 13/3/12 c 1200
19/3/12 F 3000 13/3/12 D 1100
31/3/12 G 3000 null null 0
can any body help me.or give some suggestion is it write way to get it or i can try with 2 diffrent query.
thanks in advance
Break your query into two separate queries, one for credit and another for debit and do a full outer join based on descending date, you can order by any column in fact. From the original query, I assumed that VOUCHER_TYPE = 1 is credits and VOUCHER_TYPE = 2 is debit.
Try this (not tested)
with CREDITS as (select TAV.VOUCHER_DATE PDATE,
MLI.DESCRIPTION ACC,
(case when TAV.VOUCHER_TYPE_ID = 1 then SUM(TVL.AMOUNT) else 0 end) CR
from t_acc_voucher tav
join t_voucher_ledger tvl
on tvl.voucher_id = tav.voucher_id
join M_LEDGER_INDEX MLI
on MLI.LEDGER_INDEX_ID = TVL.LEDGER_INDEX_ID
where MLI.VOUCHER_TYPE = 1
group by MLI.DESCRIPTION,
MLI.VOUCHER_TYPE,
TAV.VOUCHER_TYPE_ID,
TAV.VOUCHER_DATE),
debits as (select TAV.VOUCHER_DATE RDATE,
MLI.DESCRIPTION ACC,
(case when TAV.VOUCHER_TYPE_ID = 2 then SUM(TVL.AMOUNT) else 0 end) DR
from T_ACC_VOUCHER TAV
where mli.voucher_type = 2
join t_voucher_ledger tvl
on tvl.voucher_id = tav.voucher_id
join M_LEDGER_INDEX MLI
on MLI.LEDGER_INDEX_ID = TVL.LEDGER_INDEX_ID
group by MLI.DESCRIPTION,
MLI.VOUCHER_TYPE,
TAV.VOUCHER_TYPE_ID,
TAV.VOUCHER_DATE )
select T1.PDATE, T1.ACC, T1.CR, T2.RDATE, T2.ACC, T2.DR
from (select a.*, row_number() over (order by a.PDATE) RN
from credits a) T1
full outer join
select b.*, row_number() over (order by b.RDATE) RN
from debits b) T2
on (t1.rn = t2.rn);
select t1.date1, t1.acc1, t1.cr, t2.date2, t2.acc2, t2.dr
from (the table or query) t1
join (the table or query) t2 on t1.acc1=t2.acc2
where t1.acc1 is not null
we join the same query (or table) twice joinint to itself by acc.

select from 2 tables with multiple counts

I have 2 tables I'm trying to join in a select query.
Table 1: Store, primary_key(id,store_num)
store_id store_num due_date manager_id
1 100 06-30-2024 user1
2 108 06-30-2018 user2
3 109 13-31-2014 user3
Table 2: Department, where status(A-applied,p-Pending)
store_id store_num dept_num status
1 100 201 A
1 100 202 A
1 100 203 P
1 100 204 A
1 100 205 P
1 100 206 A
Expecting to select store_id, store_num, due_date, manager_id, Applied count, pending count. The result is something looks like this.
store_id store_num due_date manager_id applied_count pending_count
1 100 06-30-2024 user1 4 2
I tried it and got where I am able to join and get it in multiple rows, But counts not working out for me. can some one help me how I can get the counts
select
store.store_id,
store.store_num,
store.due_date,
store.manager_id,
dept.status
from store as store
inner join department as dept on store.store_id = dept.store_id
and store.store_num = dept.store_num
Your query is half way done. You need to do an aggregation to get the values in different columns. This is a conditional aggregation, as shown here:
select s.store_id, s.store_num, s.due_date, s.manager_id,
sum(case when d.status = 'A' then 1 else 0 end) as Active_Count,
sum(case when d.status = 'P' then 1 else 0 end) as Pending_Count
from store s inner join
department as dept
on s.store_id = d.store_id and s.store_num = d.store_num
group by store.store_id, store.store_num, store.due_date, store.manager_id;
The expression:
sum(case when d.status = 'A' then 1 else 0 end) as Active_Count,
Is counting the rows where status = 'A'. It does so by assigning such rows a value of 1 and then summing up that value.