SQL First row only following aggregation Rules - sql

I need to select only the most significant value from a table. Using Postgre SQL (last version) Follows the data sample:
Table Company
Id, Name, ExternalId, StartAt
1 Comp1 54123 21/05/2000
2 Comp2 23123 21/05/2000
Table Address
Id, Company, Address_Type, City
1 1 7 A
2 2 2 B
3 2 62 C
Table Adress_Type
Id, Name, importance_order
62 Adt1 1
7 Adt2 2
2 Adt3 2
What i need to do is to get the company and its major Address, based on the "importance_order". There is already a function that returns this result:
Create function~~~~
Select * from Company c
join Address a on c.address_id = a.id
Join AddressType at on a.adresstype_id = at.id
ORDER by at.importance_order
Limit 1
My problem now is that this function is called one time for every row in the query, and it take so much time (about 20 min.). Should it be possible to do this similar aproach by joinning tables? I need this join to get the First "most important"address, and then get the City name, but need to do this in a "faster" way. I need to reduce subquery`s number to its minimal.
Select * from table t
inner join Company c on t.company_id = c.id
left join address a on (c.company_id = c.id)
left join addresstype at on (a.adresstype_id = at.id)
where at.id = (
select max(id) from addresstype
where adresstype in (
select adresstype from adress where company_id = c.id
)
)
If it is not clear tell me that i get more into details.
Thanks.

For this you need PostgreSQL 8.4+ I suppose
SELECT T.*
FROM TABLE AS T
INNER JOIN
(
SELECT * FROM
(
SELECT C1.*, ROW_NUMBER() OVER(partition by C1.ID ORDER BY T.IMPORTANCE_ORDER) AS RN
FROM COMPANY AS C1
INNER JOIN ADDRESS AS A
ON C1.ID = A.COMPANY
INNER JOIN ADDRESS_TYPE AS T
ON T.ID = A.ADDRESS_TYPE_ID
) A
WHERE RN = 1
) AS B
ON B.ID= T.COMPANY_ID

Related

SQL Server: how to limit results based on query

I have a customer table, and a customer address table but I want to limit it by just one address. so even though a customer has two postal addresses I want to show only one for each customer. Forgive me if this is a silly question, I am new to programming
custid forename surname
---------------------------
1 Sam Supra
2 Kelly Kenwood
addid custid address addresstype
-------------------------------------------
1 1 Main street POSTAL
2 1 2nd Main street POSTAL
3 1 0712456254 Mobile
4 1 0526545686 LANDLINE
5 2 Second Street POSTAL
6 2 04756325654 Mobile
7 2 058654236545 LANDLINE
Query:
SELECT a.*
FROM dbo.customers a
LEFT OUTER JOIN dbo.addresses b ON a.custid = b.custid
WHERE b.addresstype = 'POSTAL'
You can use ROW_NUMBER():
SELECT c.*, a.*
FROM dbo.customers c LEFT JOIN
(SELECT a.*,
ROW_NUMBER() OVER (PARTITION BY a.custid ORDER BY a.addid DESC) as seqnum
FROM dbo.addresses a
WHERE a.addresstype = 'POSTAL'
) a
ON a.custid = c.custid AND seqnum = 1;
Notes:
Don't use arbitrary table aliases. Use abbreviations for the table names.
Presumably, you want to select the addresses, so I changed the SELECT clause.
This returns all customers, even those with no address. I am guessing that is your intention, although your query would return only customers with a postal address.
I assume you are interested in the last address entered:
SELECT
*
FROM
dbo.customers a
LEFT JOIN
dbo.addresses b on b.id =
(
SELECT
x.id
FROM
dbo.addresses x
WHERE
x.custid = a.custid
AND
x.addresstype = 'POSTAL'
ORDER BY
x.id DESC
)
SELECT c.*,
a.*
FROM dbo.customers c
LEFT JOIN (
SELECT a.*,
ROW_NUMBER() OVER (
PARTITION BY a.custid
ORDER BY a.addid DESC
) as seqnum
FROM dbo.addresses a
WHERE a.addresstype = 'POSTAL'
) a
ON a.custid = c.custid AND seqnum = 1;

Getting oldest Date SQL Complexity

I have a problem which I cannot resolve no matter what without using code, instead of SQL SCRIPT.
I have 2 tables
Person
ID Name Type
1 A A1
2 B A2
3 C A3
4 D A4
5 E A6
PersonHomes
HOMEID Location PurchaseDate PersonID
1 CA 20160101 1
2 CT 20160202 1
3 DT 20160101 2
4 BT 20170102 3
5 CT 20160303 1
6 CA 20160101 2
PersonID is foreign key of Person Table
There are no other rowz in the tables
So, we have to show detail of EACH person WITH home
The rule to write output is
IF Person has SINGLE entry in PersonHomes then use it
IF Person has MORE than ONE entry in PersonHomes then we have to look at purchase date, IF they are different then USE the PersonHomes ROW with OLDEST date in it. AND DELETE OTHER ROWS OF HIM
IF Person has MORE than ONE entry in PersonHomes then we have to look at purchase date, and IF DATES are SAME then USE the ROW with LOWER ID AND DELETE THE OTHER ROWS of HIM
This is very easy to do in code but using SQL it is complex
What I tried was to
WITH PERSON (
SELECT * FROM Person)
SELECT * FROM PERSON
INNER JOIN PersonHomes ON Person.ID = PersonHomes.PersonID
WHERE PersonHomes.PersonID = CASE WHEN (COUNT (*) FROM PersonHomes...)
Then I think I can write SQL function ?
I am stuck, Please help!
SAMPLE OUTPUT for PERSON A
ID NAME Type HOMEID Location PurchaseDate
1 A A1 5 CT 20160303
For PERSON B
ID NAME Type HOMEID Location PurchaseDate
1 A A2 3 DT 20160101
Aiden
It is not so easy to get desired output with SQL. we should write more than one sql queries.
First I created a temp table which consists of home details:
select PersonID, count(*) as HomeCount, count(distinct PurchaseDate) as
PurchaseDateCount, min(PurchaseDate) oldestPurchaseDate, min(HOMEID) as
LowerHomeID into #PersonHomesAbstractTable from PersonHomes group by PersonID
Then for the output of your first rule:
select p.ID, p.NAME, p.Type, ph.HOMEID, ph.Location, ph.PurchaseDate from Person p
inner join #PersonHomesAbstractTable a on p.ID = a.PersonID
inner join PersonHomes ph on p.ID = ph.PersonID
where a.HomeCount = 1
For the output of your second rule:
select p.ID, p.NAME, p.Type, ph.HOMEID, ph.Location, ph.PurchaseDate
from Person p inner join #PersonHomesAbstractTable a on p.ID = a.PersonID
inner join PersonHomes ph on p.ID = ph.PersonID and
ph.PurchaseDate = a.oldestPurchaseDate
where a.HomeCount > 1 and a.PurchaseDateCount <> 1
And finally for the output of your third rule:
select p.ID, p.NAME, p.Type, ph.HOMEID, ph.Location, ph.PurchaseDate
from Person p inner join #PersonHomesAbstractTable a on p.ID = a.PersonID
inner join PersonHomes ph on p.ID = ph.PersonID and
ph.HOMEID = a.LowerHomeID
where a.HomeCount > 1 and a.PurchaseDateCount = 1
Of course there are some other ways, but now this way is come to my mind.
If you want to delete undesired rows, you can use scripts below:
delete from PersonHomes where HOMEID in
(
select ph.HOMEID from #PersonHomesAbstractTable a
inner join PersonHomes ph on a.PersonID = ph.PersonID and
ph.PurchaseDate <> a.oldestPurchaseDate
where a.HomeCount > 1 and a.PurchaseDateCount <> 1
union
select p.HOMEID from #PersonHomesAbstractTable a
inner join PersonHomes ph on a.PersonID = ph.PersonID and
ph.HOMEID <> a.LowerHomeID
where a.HomeCount > 1 and a.PurchaseDateCount = 1
)
You seem to have a prioritization query. I would solve this using row_number():
select ph.*
from (select ph.*,
row_number() over (partition by personid
order by purchasedate asc, homeid asc
) as seqnum
from personhomes ph
) ph
where seqnum = 1;
This doesn't actually change the data in the table. Although you say delete, it seems like you just want a result set with one home per person.
This is shortest approach got by Link
;WITH cte AS
(
SELECT *, RowN = ROW_NUMBER() OVER (PARTITION BY ID ORDER BY AddressMoveDate DESC) FROM Address
)
DELETE FROM cte WHERE RowN > 1

SQL query to find a record which has all matching records in another table

I have below 3 tables and I want to write a SQL query which will list the store present in all city: (here the result should be "Walmart")
Stores:
ID Name
1 Walmart
2 Target
3 Sears
Stores_City
ID Store_id City ID
1 1 10
2 1 20
3 2 10
4 1 30
City
ID Name
10 NewYork
20 Boston
30 Eagan
I am unable to find a query that works. Any help is appreciated!
select s.Name
from Stores s
inner join
(
select store_id, count(distinct city_id)
from stores_city
group by store_id
having count(distinct city_id) = (select count(*) from City)
) x
on x.store_id = s.id;
You can do it by grouping on store_id and checking for the count from stores table.
A straight join would work
Select distinct s.name from stores s inner join store _city SC on s.id=sc.id
Inner join city c on
Sc.city_id = c.id
Here is another way that will work:
select s.*
from stores s
where not exists (
select c.id
from city c
except
select sc.city_id
from stores_city sc
where sc.store_id = s.id
)
Try this:
SELECT
s.Name
FROM Stores s
WHERE NOT EXISTS (SELECT TOP 1
1
FROM City c
LEFT JOIN Stores_City sc
ON c.ID = sc.CityID
AND sc.Store_id = s.ID
WHERE sc.ID IS NULL)

Oracle DB Just Combine Two tables Side By Side

1:Table COUNTRY
ID NAME
1 USA
2 BRITAIN
2:Table PEOPLE
P_ID P_NAME
4 JOHN
5 MONTY
Resultant table should have
ID NAME P_ID P_NAME
1 USA 4 JOHN
2 BRITAIN 5 MONTY
Just side by side merge. Is it possible? LIKE TABLE1 | TABLE2
Example
with x as (
select id, name, rownum rn from country
order by id),
y as(
select p_id, p_name, rownum rn from people
order by p_id)
select id,name,p_id,p_name from x
left join y on y.rn = x.rn
union
select id,name,p_id,p_name from y
left join x on y.rn = x.rn
If I well understood your question this could work:
SELECT *
FROM (SELECT T1.*,ROWNUM AS R FROM COUNTRY T1) V1,
(SELECT T2.*,ROWNUM AS R FROM PEOPLE T2) V2
WHERE V1.R = V2.R(+)
But you need to know wich table is bigger and I fear you aren't going to to get reliable results
This should work..
select c.id,c.name, p.p_id, p.p_name from country c inner join people p on c.id = p.p_id;
Try this:
SELECT * FROM COUNTRY C
INNER JOIN PEOPLE P ON C.ID = P.P_ID;
Updated answer :
Select * from #country as c , #people as p where p_id =4 and id=1 OR id = 2 and p_id=5

SQL Sum of rows grouped by id

This SQL:
select Name,
(select COUNT(1) from tbl_projects where statusId = tbl_sections.StatusId) as N
from tbl_sections
left join tbl_section_names on tbl_section_names.Id = NameId
Generates the follows data:
Name N
Completed 133
Cancelled 100
Unassigned 1
Sales 49
Development 10
Development 4
Development 1
I'm trying to modify it so it returns the data as follows:
Name N
Completed 133
Cancelled 100
Unassigned 1
Sales 49
Development 15
(ie, sum up the rows where the name is the same)
Can anyone suggest some clues on how to make this work ? I'm guessing I need a SUM and a GROUP BY, but it never even runs the query as all I get are errors.
Try this query. It sums N grouped by Name.
SELECT Name, SUM(N)
FROM (
SELECT Name,
(SELECT COUNT(1)
FROM tbl_projects
WHERE statusId = tbl_sections.StatusId
) AS N
FROM tbl_sections
LEFT JOIN tbl_section_names ON tbl_section_names.Id = NameId
) a
GROUP BY a.Name
Try this
select Name, count(p.statusid) N
from tbl_sections
left join tbl_section_names on tbl_section_names.Id = NameId
left outer join tbl_projects p on tbl_sections.StatusId = p.statusId
group by Name
Select Name, Sum(N) from
(select Name,
(select COUNT(1) from tbl_projects where statusId = tbl_sections.StatusId) as N
from tbl_sections
left join tbl_section_names on tbl_section_names.Id = NameId)
group by Name
This query is giving you count per status, which means Development has sections with three different status's, and the query would reflect this and make more sense if you added the status as a column:
select Name, tbl_sections.StatusId,
(select COUNT(1) from tbl_projects where statusId = tbl_sections.StatusId) as N
from tbl_sections
left join tbl_section_names on tbl_section_names.Id = NameId
I don't know the structure of your database, but I if you want a count of the number of sections per name, might be like this. This basically will look at the result of the join, and then summarize it by telling you the number of times each unique name occurs:
select Name, count(*)
from tbl_sections
left join tbl_section_names on tbl_section_names.Id = NameId
Group By Name
Try and give this a go.
select Name,
SUM(select COUNT(1) from tbl_projects where statusId = tbl_sections.StatusId) as N
from tbl_sections
left join tbl_section_names on tbl_section_names.Id = NameId
group by Name