Require result with of multiple customers along with city name - postgresql

we have following requirement
I need customer_name which are in Active status and attribute_name=City and attribute_value in(Indore,Mumbai) and result should return count less than=2
It means from result for Indore i should get 2 results out of 3 and for Mumbai i should get 1 out of 1.
I tried below 2 ways but getting all the rows for customers which are in city Indore and Mumbai
Customer_table has below details
customer_id customer_name customer_Status
------------------------------------------
1 ABC Active
2 XYZ Active
3 PQR NA
4 ABCD Active
4 ABCDE Active
customer_details table has below details
customer_id attribute_name attribute_value
------------------------------------------
1 City Indore
1 Phone Number 9100000000
1 Country India
2 City Mumbai
2 Phone Number 9100000001
2 Country India
3 City Delhi
3 Phone Number 9100000002
3 Country India
4 City Mumbai
4 Phone Number 9100000003
4 Country India
5 City Mumbai
5 Phone Number 9100000004
5 Country India
Code:-
select attribute_value, r.customer_name from customer_details res
join lateral (
select customer_name from Customer_table
where res.customer_id=customer_id
and customer_Status= 'Active'
limit 2
) r on true
where attribute_name= 'City' and attribute_value in ('Indore','Mumbai');
Code:-
SELECT s.customer_name,attribute_value
FROM (
SELECT *, row_number() OVER (PARTITION BY customer_id ) AS rn
FROM customer_details
WHERE attribute_name= 'City' and attribute_value in ('Indore','Mumbai')
) e
JOIN Customer_table s USING (customer_id)
WHERE rn <= 2
and and customer_Status= 'Active'
ORDER BY customer_id, e.rn;

Please try this way.
select customer_name,attribute_value from (
select ct.customer_name,attribute_value,row_number() OVER (PARTITION BY attribute_value ) AS rn
from customer_table ct ,customer_details cd
where ct.customer_status = 'Active' and ct.customer_id = cd.customer_id
and attribute_name='City' and attribute_value in('Indore','Mumbai')
) as t
where rn <= 2

This design will kill any performance you hope to get around customers. The first thing I would do in drop the customer_details table and the columns phone_number, city, and country to the customers table. Lacking that I would create a view that joined customers to customer_details having all the columns in the view.
create view Customer_Standard as
select c.cust_id, c.name, c.status, ph.attribute_value phone_number, ct.attribute_value city, cn.attribute_value country
from customers c
left join customer_details ph on (ph.cust_id = c.cust_id and ph.attribute_name = 'Phone Number')
left join customer_details ct on (ct.cust_id = c.cust_id and ct.attribute_name = 'City')
left join customer_details cn on (cn.cust_id = c.cust_id and cn.attribute_name = 'Country') ;
Then the query you want becomes:
select cust_id, name, status, phone_number, city, country
from (select cs.*, row_number() over (partition by city order by cust_id) rn
from Customer_Standard cs
) cust
where city in ('Indore','Mumbai')
and rn<3;

Related

Postgresql group by relation

I want to group the records by relation.
products table:
id
price
1
100
2
200
3
300
4
400
product_properties table:
id
productId
propertyId
1
1
2
2
1
3
3
2
2
4
2
3
5
3
4
6
4
4
The query should select lowest price group by product_properties. I mean, If products have same properties in product_properties, query should return product that has lowest price.
So, For these tables query should return products that have ids 1,3.
I use TypeORM, I tried join the relation and distinct on relation alias name but its not worked.
How can I achieve this?
I wrote two variants query for you:
-- variant 1
select distinct t1.product_id from (
select
pr.price, pp.product_id, pp.property_id, min(pr.price) OVER(PARTITION BY pp.property_id) as min_price
from
test.product_properties pp
inner join
test.products pr on pp.product_id = pr.id
) t1
where
t1.price = t1.min_price;
-- variant 2
select distinct t1.product_id from test.product_properties t1
inner join test.products t2 on t1.product_id = t2.id
inner join (
select
pp.property_id, min(pr.price) as min_price
from
test.product_properties pp
inner join
test.products pr on pp.product_id = pr.id
group by pp.property_id
) t3 on t3.property_id = t1.property_id and t3.min_price = t2.price;

can you helpe me to display the latest data on each group

I have this datatables:
table1
id category
-------------
1 a
2 b
3 c
table2
id heading category_id
----------------------
1 name 1
2 adddress 2
3 phone 3
4 email 1
I want to group this table and display the latest data for that the following query was I used:
SELECT news.id,news.image,news.heading,news.description,
news.date,news.category_id,categories.category
FROM `news`
INNER JOIN categories On news.category_id=categories.id
group by category_id
But I didnt get the latest data that I entered.
Try the query below:
SELECT *
FROM table2 AS tb2 LEFT JOIN table1 AS tb1 ON tb2.category_id = tb1.id
ORDER BY tb1.id
GROUP BY tb2.category_id

Postgres how to maintain order of rows using CTEs

I have 2 tables
students:
id | name | age
1 abc 20
2 xyz 21
scores:
id | studentid | marks
1 1 20
2 2 22
3 2 20
4 1 22
5 1 20
where studentid is foreign key to students table
When a do
select studentid
from scores
where marks=20;
I get the following result
1, 2, 1
But if want the name of the student name and when I do a join using
select t1.name
from students t1
inner join scores t2 on t1.id = t2.studentid
where t2.marks=20;
I get xyz,abc,abc Though the ouput is correct is there any way I can maintain the order in which scores are listed in the scores table? I should get abc,xyz,abc as output. I tried using subquery as well
SELECT name
FROM students
WHERE ID IN ( select studentid from scores where marks=20) ;
but that also did not give me correct order. How can this be achieved using CTEs (common table expressions)? I tried the follownig cte but it did not work
with cte as(
select t2.id, t1.name
from students t1
inner join scores t2 on t1.id = t2.studentid
where t2.marks=20)
select name from cte order by id
You can order by a column not present in select list:
select t1.name
from students t1
inner join scores t2 on t1.id = t2.student_id
where t2.marks=20
order by t2.id;
name
------
abc
xyz
abc
(3 rows)

Make a column values header for rest of columns using TSQL

I have following table
ID | Group | Type | Product
1 Dairy Milk Fresh Milk
2 Dairy Butter Butter Cream
3 Beverage Coke Coca cola
4 Beverage Diet Dew
5 Beverage Juice Fresh Juice
I need following output/query result:
ID | Group | Type | Product
1 Dairy
1 Milk Fresh Milk
2 Butter Butter Cream
2 Beverage
1 Coke Coca cola
2 Diet Dew
3 Juice Fresh Juice
For above sample a hard coded script can do the job but I look for a dynamic script for any number of groups. I do not have any idea how it can be done so, I do not have a sample query yet. I need ideas, examples that at least give me an idea. PIVOT looks a close option but does not looks to be fully fit for this case.
Here's a possible way. It basically unions the "Group-Headers" and the "Group-Items". The difficulty was to order them correctly.
WITH CTE AS
(
SELECT ID,[Group],Type,Product,
ROW_NUMBER() OVER (PARTITION BY [Group] Order By ID)AS RN
FROM Drink
)
SELECT ID,[Group],Type,Product
FROM(
SELECT RN AS ID,[Group],[Id]AS OriginalId,'' As Type,'' As Product, 0 AS RN, 'Group' As RowType
FROM CTE WHERE RN = 1
UNION ALL
SELECT RN AS ID,'' AS [Group],[Id]AS OriginalId,Type,Product, RN, 'Item' As RowType
FROM CTE
)X
ORDER BY OriginalId ASC
, CASE WHEN RowType='Group' THEN 0 ELSE 1 END ASC
, RN ASC
Here's a demo-fiddle: http://sqlfiddle.com/#!6/ed6ca/2/0
A slightly simplified approach:
With Groups As
(
Select Distinct Min(Id) As Id, [Group], '' As [Type], '' As Product
From dbo.Source
Group By [Group]
)
Select Coalesce(Cast(Z.Id As varchar(10)),'') As Id
, Coalesce(Z.[Group],'') As [Group]
, Z.[Type], Z.Product
From (
Select Id As Sort, Id, [Group], [Type], Product
From Groups
Union All
Select G.Id, Null, Null, S.[Type], S.Product
From dbo.Source As S
Join Groups As G
On G.[Group] = S.[Group]
) As Z
Order By Sort
It should be noted that the use of Coalesce is purely for aesthetic reasons. You could simply return null in these cases.
SQL Fiddle
And an approach with ROW_NUMBER:
IF OBJECT_ID('dbo.grouprows') IS NOT NULL DROP TABLE dbo.grouprows;
CREATE TABLE dbo.grouprows(
ID INT,
Grp NVARCHAR(MAX),
Type NVARCHAR(MAX),
Product NVARCHAR(MAX)
);
INSERT INTO dbo.grouprows VALUES
(1,'Dairy','Milk','Fresh Milk'),
(2,'Dairy','Butter','Butter Cream'),
(3,'Beverage','Coke','Coca cola'),
(4,'Beverage','Diet','Dew'),
(5,'Beverage','Juice','Fresh Juice');
SELECT
CASE WHEN gg = 0 THEN dr1 END GrpId,
CASE WHEN gg = 1 THEN rn1 END TypeId,
ISNULL(Grp,'')Grp,
CASE WHEN gg = 1 THEN Type ELSE '' END Type,
CASE WHEN gg = 1 THEN Product ELSE '' END Product
FROM(
SELECT *,
DENSE_RANK()OVER(ORDER BY Grp DESC) dr1
FROM(
SELECT *,
ROW_NUMBER()OVER(PARTITION BY Grp ORDER BY type,gg) rn1,
ROW_NUMBER()OVER(ORDER BY type,gg) rn0
FROM(
SELECT Grp,Type,Product, GROUPING(Grp) gg, GROUPING(type) tg FROM dbo.grouprows
GROUP BY Product, Type, Grp
WITH ROLLUP
)X1
WHERE tg = 0
)X2
WHERE gg=1 OR rn1 = 1
)X3
ORDER BY rn0

Merging tables in t-sql

I have a table holding periods and prices, something like this
itemid periodid periodstart periodend price
1 1 2011/01/01 2011/05/01 50.00
1 2 2011/05/02 2011/08/01 80.00
1 3 2011/08/02 2011/12/31 50.00
Now I have a second table that can hold single dates or periods
itemid periodid periodstart periodend price
1 8 2011/07/01 2011/07/17 70.00
Now, how can I do a query that would return the following result?
itemid periodid periodstart periodend price
1 1 2011/01/01 2011/05/01 50.00
1 2 2011/05/02 2011/06/30 80.00 ****
1 8 2011/07/01 2011/07/17 70.00 ***
1 2 2011/07/18 2011/08/01 80.00 ****
1 3 2011/08/02 2011/12/31 50.00
EDIT -- Highlight the fact that the merge is modifying the dates around it
How about something like
select
t1.itemid,t1.periodid,t1.periodstart, coalesce(dateadd(d,-1,t2.periodstart),t1.periodend) as periodend, t1.price
from t1
left outer join t2 on t1.periodstart < t2.periodstart and t1.periodend>t2.periodstart and t1.itemid=t2.itemid
union
select
t2.itemid,t2.periodid,t2.periodstart, t2.periodend, t2.price
from t1
inner join t2 on t1.periodstart < t2.periodstart and t1.periodend>t2.periodstart and t1.itemid=t2.itemid
union
select
t1.itemid,t1.periodid,dateAdd(d,1,t2.periodend), t1.periodend, t1.price
from t1
inner join t2 on t1.periodstart < t2.periodend and t1.periodend>t2.periodend and t1.itemid=t2.itemid
order by periodstart
Use a Union?
Select itemid, periodid,periodstart, periodend,price FROM table1
UNION
SELECT itemid, periodid,periodstart, periodend,price FROM table2
Are you trying to do some sort of join though? the result set doesn't match the two tables you supplied.
Are you accounting for entries that line up or are you just trying to combine the rows?
if the latter, you could just do a Union
Select itemid, periodid, periodstart, periodend, price
From Table1
Union
Select itemid, periodid, periodstart, periodend, price
From Table2