Reducing lines and readability in SQL query - sql

I have 2 tables namely:
list_a:
ID Date pg
23 2016-11-30 sub
23 2016-12-03 sub
23 2016-12-04 sub
23 2016-12-05 sub
69 2017-07-21 Closed
69 2017-07-22 Closed
69 2017-07-23 Closed
92 2016-11-29 sub
92 2016-11-30 sub
46 2017-01-11 In
46 2017-01-12 In
46 2017-01-13 In
list_b:
ID Name
23 B
46 B
69 X
92 B
My task is to write an sql query such that I get count of each date with name 'B' and the count should be greater than 4.
So far I have written the following query which gives me the output:
SELECT date, count(Name) as CountName FROM
(
SELECT list_b.ID as Id,list_b.Name as Name,Date from list_b
inner JOIN list_a
on list_b.ID=list_a.ID
) t
where Name='B'
group by Date
Having count(Name)>4
order by count(Name) desc;
I am trying to make my query more readable and less chunkier.
Is there a better way that you can suggest to solve this problem?
Note: the above tables are snippets of the original tables!

You have a spare subquery that isn't necessary. Also a personal preference to use an alias rather than table names (on a table called t_databaseassociation this does make a big difference)
Try
SELECT date, count(name) as countname
from list_b b
inner JOIN list_a a on b.ID= a.ID
where Name='B'
group by Date
Having count(Name)>4
order by count(Name) desc;

unfortunately you can't use CTE with clause in mysql, but you can create temp table:
CREATE TEMPORARY TABLE IF NOT EXISTS t AS (
SELECT list_b.ID AS Id,list_b.Name AS Name,Date
FROM list_b
INNER JOIN list_a ON list_b.ID=list_a.ID
);
SELECT date, count(Name) AS CountName
FROM t
WHERE Name='B'
GROUP BY Date HAVING count(Name)>4
ORDER BY count(Name) DESC;

SELECT `Date`, COUNT(Name) as CountName
FROM list_a
NATURAL JOIN list_b
WHERE Name = 'B'
GROUP BY `Date`
HAVING COUNT(Name) > 4
ORDER BY CountName DESC;

SELECT
list_b.Date AS date,
count(Name) AS CountName
FROM list_b
INNER JOIN list_a ON list_b.ID=list_a.ID
WHERE list_b.Name = 'B'
GROUP BY list_b.Date HAVING count(list_b.Name) > 4
ORDER BY count(Name) DESC

Related

Select based on max date from another table

I'm trying to do a simple Select query by getting the country based on the MAX Last update from the other table.
Order#
1
2
3
4
The other table contains the country and the last update:
Order# Cntry Last Update
1 12/21/2019 9:19 PM
1 US 1/10/2020 1:07 AM
2 JP 7/29/2020 12:15 PM
3 CA 4/12/1992 2:04 PM
3 GB 11/6/2001 9:26 AM
3 DK 2/1/2005 3:04 AM
4 CN 8/20/2013 12:04 AM
4 10/1/2015 4:04 PM
My desired result:
Order# Country
1 US
2 JP
3 DK
4
Not sure the right solution for this. So far i'm stuck with this:
SELECT Main.[Order#], tempTable.Cntry
FROM Main
LEFT JOIN (
SELECT [Order#], Cntry, Max([Last Update]) as LatestDate FROM Country
GROUP BY [Order#], Cntry
) as tempTable ON Main.[Order#] = tempTable.[Order#];
Thanks in advance!
If needs only number of order and country,maybe don't need two tables:
SELECT distinct order, country
FROM
(
SELECT order, LAST_VALUE (country) OVER (PARTITION by [order] order by last_update) country FROM Country
) X
In SQL Server, you can use a correlated subquery:
update main
set country = (select top (1) s.country
from secondtable s
where s.order# = main.order#
order by s.lastupdate desc
);
EDIT:
A select would look quite simimilar:
select m.*,
(select top (1) country
from secondtable s
where s.order# = main.order#
order by s.lastupdate desc
)
from main m
I don't have time to try it with sample data, but is that what you are looking for?
select order orde, cntry
from table
where last_update =
(select max(last_update) from table where order = orde)

Top 2 Months of Sales by Customer - Oracle

I am trying to develop a query to pull out the top 2 months of sales by customer id. Here is a sample table:
Customer_ID Sales Amount Period
144567 40 2
234567 50 5
234567 40 7
144567 80 10
144567 48 2
234567 23 7
desired output would be
Customer_ID Sales Sum Period
144567 80 10
144567 48 2
234567 50 5
234567 40 7
I've tried
select sum(net_sales_usd_spot), valid_period, customer_id
from sales_trans_price_output
where valid_period in (select valid_period, sum(net_sales_usd_spot)
from sales_trans_price_output
where rank<=2)
group by valid_period, customer_id
error is
too many values ORA-00913.
I see why, but not sure how to rework it.
Try:
SELECT *
FROM (
SELECT t.*,
row_number() over (partition by customer_id order by sales_amount desc ) rn
FROM sales_trans_price t
)
WHERE rn <= 2
ORDER BY 1,2 desc
Demo: http://sqlfiddle.com/#!4/882888/3
what if you change your where clause to:
where valid_period in
(
select p.valid_period from sales_trans_price_output p
join (select valid_period, sum(net_sales_usd_spot)
from sales_trans_price_output
where rank<=2) s on s.valid_period = p.valid_period
)
It might be ugly and need refactoring, but I think this is the logic you're after.
The error is because of this.
where valid_period in (select valid_period, sum(net_sales_usd_spot)
from sales_trans_price_output
where rank<=2)
The subquery can only contain one field.
You are on the right track using rank, but you might not be using it correctly. Google oracle rank to find the correct syntax.
Back to what you are looking to achieve, a derived table is the approach I would use. That's simply a subquery with an alias. Or, if you use the keyword with, it might be called a CTE - Computed Table Expression.
Try it
SELECT * FROM (
SELECT T.*,
RANK () OVER (PARTITION BY CUSTOMER_ID
ORDER BY VALID_PERIOD DESC) FN_RANK
FROM SALES_TRANS_PRICE_OUTPUT T
) A
WHERE A.FN_RANK <= 2
ORDER BY CUSTOMER_ID ASC, VALID_PERIOD DESC, FN_RANK DESC

Eliminate a duplicate result from a single column

Let's say I have a result from a query that looks like this:
ContactID LeadSalePrice
---------------------------
45 19.90
45 18.00
32 17.50
But, I want to eliminate duplicate ContactID's, always taking the higher price result. So what I want is:
ContactID LeadSalePrice
---------------------------
45 19.90
32 17.50
Here's (a simplified version of) the query:
SELECT
sc.ContactID
, c.LeadSalePrice
FROM
LeadSalesCampaignCriterias c
JOIN LeadSalesCampaigns sc ON c.LeadSalesCampaignID = sc.LeadSalesCampaignID
WHERE
...
ORDER BY
LeadSalePrice DESC
I've been playing around with DISTINCT and GROUP BY, but I'm not getting it.
Just use GROUP BY:
SELECT sc.ContactID, MAX(c.LeadSalePrice) as LeadSalePrice
FROM LeadSalesCampaignCriterias c JOIN
LeadSalesCampaigns sc
ON c.LeadSalesCampaignID = sc.LeadSalesCampaignID
WHERE ...
GROUP BY sc.ContactID;
Another option is the WITH TIES and Row_Number()
Select Top 1 with Ties *
From YourTable
Order By Row_Number() over (Partition By ContactID Order By LeadSalePrice Desc)
Returns
ContactID LeadSalePrice
32 17.50
45 19.90

Firebird Query- Return first row each group

In a firebird database with a table "Sales", I need to select the first sale of all customers. See below a sample that show the table and desired result of query.
---------------------------------------
SALES
---------------------------------------
ID CUSTOMERID DTHRSALE
1 25 01/04/16 09:32
2 30 02/04/16 11:22
3 25 05/04/16 08:10
4 31 07/03/16 10:22
5 22 01/02/16 12:30
6 22 10/01/16 08:45
Result: only first sale, based on sale date.
ID CUSTOMERID DTHRSALE
1 25 01/04/16 09:32
2 30 02/04/16 11:22
4 31 07/03/16 10:22
6 22 10/01/16 08:45
I've already tested following code "Select first row in each GROUP BY group?", but it did not work.
In Firebird 2.5 you can do this with the following query; this is a minor modification of the second part of the accepted answer of the question you linked to tailored to your schema and requirements:
select x.id,
x.customerid,
x.dthrsale
from sales x
join (select customerid,
min(dthrsale) as first_sale
from sales
group by customerid) p on p.customerid = x.customerid
and p.first_sale = x.dthrsale
order by x.id
The order by is not necessary, I just added it to make it give the order as shown in your question.
With Firebird 3 you can use the window function ROW_NUMBER which is also described in the linked answer. The linked answer incorrectly said the first solution would work on Firebird 2.1 and higher. I have now edited it.
Search for the sales with no earlier sales:
SELECT S1.*
FROM SALES S1
LEFT JOIN SALES S2 ON S2.CUSTOMERID = S1.CUSTOMERID AND S2.DTHRSALE < S1.DTHRSALE
WHERE S2.ID IS NULL
Define an index over (customerid, dthrsale) to make it fast.
in Firebird 3 , get first row foreach customer by min sales_date :
SELECT id, customer_id, total, sales_date
FROM (
SELECT id, customer_id, total, sales_date
, row_number() OVER(PARTITION BY customer_id ORDER BY sales_date ASC ) AS rn
FROM SALES
) sub
WHERE rn = 1;
İf you want to get other related columns, This is where your self-answer fails.
select customer_id , min(sales_date)
, id, total --what about other colums
from SALES
group by customer_id
So simple as:
select CUSTOMERID min(DTHRSALE) from SALES group by CUSTOMERID

How to sum in sql with a DISTINCT clause

In the following example SQL Fiddle
How should I proceed to obtain the cumulative price for each 'Phone' instead of obtaining the last value?
In the example given below, I would need the following table to be produced:
Phone Price Purchases
50 35 3
51 50 2
52 99 3
55 21 2
53 16 2
54 21 1
56 16 1
58 22 1
57 10 2
This is to be done in SQL-Server 2012
Thanks in advance.
You should be able to use the following:
select c1.phone,
c2.TotalPrice,
c1.purchases
from supportContacts c1
inner join
(
select
max(Fecha) maxFecha,
sum(price) TotalPrice,
phone
from supportContacts
group by phone
) c2
on c1.phone = c2.phone
and c1.Fecha = c2.maxFecha
order by c1.phone;
See SQL Fiddle with Demo.
The subquery gets the Total sum for each phone along with the the max fecha associated with the phone. You then use this and join back to your table on both the phone and the fecha to get the result.
I don't have a SQL Server 2012 handy, but give this a shot:
select
phone,
purchases,
price,
sum(price) over (partition by phone order by phone, price) as running_sum_purchases
FROM
supportContacts
Isn't it just...
SELECT Phone, Sum(Price), Count(Purchases)
FROM supportContacts
GROUP BY Phone
ORDER BY 1
.. or have I missed something?
http://sqlfiddle.com/#!6/7b36f/41
50 35 3
51 50 4
52 99 3
53 16 2
54 21 2
55 21 1
56 16 1
57 10 1
58 22 2
If you need more details per phone, you can add a subquery :
SELECT
Phone,
Sum(Price) as Total,
Count(Purchases) as Purchase_Count,
(SELECT TOP 1 Price
FROM supportContacts sc2
WHERE sc2.phone=sc1.phone
ORDER BY fecha DESC
) as Most_Recent
FROM supportContacts sc1
GROUP BY Phone
ORDER BY Phone
or, for the actual requirement which I've finally worked out :)
SELECT
Phone,
Sum(Price) as Total,
Count(Purchases) as Purchase_Count,
(SELECT Purchases
FROM supportContacts sc2
WHERE sc2.phone=sc1.phone
AND sc2.Fecha=
(SELECT Max(Fecha)
FROM supportContacts sc3
WHERE sc3.phone=sc1.phone
)
) as Last_Purchase
FROM supportContacts sc1
GROUP BY Phone
ORDER BY Phone
.. which is starting to get quite unwieldy, there's probably an optimisation possible, but I'm losing the will to play... LOL
But thanks for the cerebral exercise of trying to do it this way :)
EDIT
I would probably have done it like this, if it had been me...
http://sqlfiddle.com/#!6/7b36f/98
With PhoneGroup as
( SELECT
Phone,
Sum(Price) as Total_Price,
Max(Fecha) as Last_Fecha
FROM supportContacts
GROUP BY Phone
)
SELECT
Phone, Total_Price,
(SELECT Purchases
FROM supportContacts sc2
WHERE sc2.phone=PhoneGroup.phone
AND sc2.Fecha=PhoneGroup.Last_Fecha
) as Last_Purchase
FROM PhoneGroup
ORDER BY Phone