Return top 3 rows per group - sql

When I made this select it shows as result all “rut” with it phone numbers, my problem
is that each “rut” has almost 10 phone numbers, and I need just 3 phone numbers for
each “rut”, I tryied using TOP, but just shows the first 3 rows of all table and not the
first rows by “rut”, how can I use TOP just for the row “rut” and not in the all the
table
Select Distinct
t1.rut_cliente as rut_cliente,
t1.nro_fono as numero
--Into #tmp_numeros
From dat_clientes_sucursales_contactos_telefonos t1,dat_rut_clientes t2
where t1.rut_cliente = t2.rut_cliente
and cod_prioridad = 1
this is what I get with this query:
Rut_cliente Nro_fono
60506000-5 2046840
60506000-5 3507935
60506000-5 4106886
60506000-5 5440000
60506000-5 5445000
81698900-0 2373281
81698900-0 3541342
81698900-0 3541438
81698900-0 3541518
81698900-0 3542101
and this is what I want:
Rut_cliente Nro_fono
60506000-5 2046840
60506000-5 3507935
60506000-5 4106886
81698900-0 2373281
81698900-0 3541342
81698900-0 3541438
thanks in advance.

The question was originally tagged SQL Server 2008, where you can do this using a common table expression:
;WITH x AS
(
SELECT Rut_cliente, Nro_fono, rn = ROW_NUMBER()
OVER (PARTITION BY Rut_cliente ORDER BY Nro_fono)
FROM dbo.dat_clientes_sucursales_contactos_telefonos AS t1
INNER JOIN dbo.dat_rut_clientes AS t2
ON t1.rut_cliente = t2.rut_cliente
WHERE cod_prioridad = 1
)
SELECT Rut_cliente, Nro_fono FROM x
WHERE rn <= 3
ORDER BY Rut_cliente, Nro_fono;
Other comments:
Please don't use table, table syntax. Use proper INNER JOINs. I explain why here.
Please add proper aliases to the inner query, so people know which columns come from t1 and which columns come from t2.
But now we learn the user is actually using SQL Server 2000. This is how I would do it there, I think, but performance is going to be horrible. I'm not 100% sure this works (because again I'm making guesses about which columns come from which table).
SELECT x.rut_cliente, x.nro_fono, COUNT(*) FROM
(
SELECT t1.rut_cliente, t1.nro_fono
FROM dat_clientes_sucursales_contactos_telefonos AS t1
INNER JOIN dat_rut_clientes AS t2
ON t1.rut_cliente = t2.rut_cliente
WHERE cod_prioridad = 1
) AS x
INNER JOIN dat_clientes_sucursales_contactos_telefonos AS b
ON b.rut_cliente = x.rut_cliente
AND b.nro_fono <= x.nro_fono
GROUP BY x.rut_cliente, x.nro_fono
HAVING COUNT(*) <= 3
ORDER BY x.rut_cliente, x.Nro_fono;

For example with ROW_NUMBER which is a window function and returns a row-number for each partition(similar to group by) determined by the order by.:
WITH CTE AS(
SELECT t1.rut_cliente as rut_cliente, t1.nro_fono as numero,
RN = ROW_NUMBER()OVER(PARTITION BY Rut_cliente ORDER BY Nro_fono)
FROM dbo.dat_clientes_sucursales_contactos_telefonos AS t1
INNER JOIN dbo.dat_rut_clientes AS t2 ON t1.rut_cliente = t2.rut_cliente
WHERE cod_prioridad = 1
)
SELECT rut_cliente as rut_cliente, nro_fono as numero
FROM CTE
WHERE RN <= 3

Try this
;with CTE AS (
select Rut_cliente, Nro_fono , ROW_NUMBER() OVER
(PARTITION BY Rut_cliente ORDER BY Nro_fono) rn
FROM dbo.dat_clientes_sucursales_contactos_telefonos AS a
INNER JOIN dbo.dat_rut_clientes AS b
ON a.rut_cliente = b.rut_cliente
WHERE cod_prioridad = 1
)
SELECT * FROM CTE where rn <=3

Related

New SQL user - I'm trying to extract the latest record in my query. Accountant new to SQL

For every AbsenceBalance.AbsenceTypesUID I want to return the latest record AbsenceBalance.BalanceTime for each AbsenceBalance.EmployeeUID
I have tried select max but it only returns the most recent entry for the entire table and not by AbsenceBalance.AbsenceTypesUID or AbsenceBalance.EmployeeUID
This is my query
SELECT TOP (1000)
AbsenceBalance.[UID],
AbsenceBalance.BalanceTime,
AbsenceBalance.AbsenceTypesUID,
AbsenceBalance.Mins,
Employee.FullName,
Employee.FirstName,
Employee.LastName,
AbsenceBalance.EmployeeUID,
absencetypes.LongName
from [RiteqDB].[dbo].[AbsenceBalance]
LEFT JOIN [RiteqDB].[dbo].Employee on AbsenceBalance.EmployeeUID = Employee.UID
LEFT JOIN [RiteqDB].[dbo].AbsenceTypes on absencebalance.AbsenceTypesUID = absencetypes.UID
where AbsenceBalance.[UID] = (select max (AbsenceBalance.[UID]) from [RiteqDB].[dbo].[AbsenceBalance] where AbsenceBalance.AbsenceTypesUID = AbsenceBalance.AbsenceTypesUID)
--where Select Max(v) from (values (AbsenceBalance.BalanceTime)
order by FullName, AbsenceTypesUID
It sounds like you might need a group by link, then either use an inner select in a where (like you have) or use this with an inner join.
SELECT
Max(AbsenceBalance.[UID]),
AbsenceBalance.AbsenceTypesUID,
AbsenceBalance.EmployeeUID,
from [RiteqDB].[dbo].[AbsenceBalance]
GROUP BY AbsenceTypesUID, EmployeeUID
;with cte as (
SELECT TOP (1000) AB.[UID]
,AB.BalanceTime
,AB.AbsenceTypesUID
,AB.Mins
,E.FullName
,E.FirstName
,E.LastName
,AB.EmployeeUID
,AT.LongName
, ROW_NUMBER() OVER(PARTITION BY AB.[UID], AB.EmployeeUID order by AB.BalanceTime DESC) AS RUN
FROM [RiteqDB].[dbo].[AbsenceBalance] AB
LEFT JOIN [RiteqDB].[dbo].Employee E ON AB.EmployeeUID = E.UID
LEFT JOIN [RiteqDB].[dbo].AbsenceTypes AT ON AB.AbsenceTypesUID = AT.UID
)
select * from cte
where RUN = 1
In your where condition your comparing with itself that might be the issue.
select max (AbsenceBalance.[UID]) from [RiteqDB].[dbo].[AbsenceBalance]
where AbsenceBalance.AbsenceTypesUID = AbsenceBalance.AbsenceTypesUID
following is wrong
AbsenceBalance.AbsenceTypesUID = AbsenceBalance.AbsenceTypesUID

SQL SERVER make a row data to column

I'm currently making a program to store data in database sql server.
and the data is a cost that divided into max 5 column cost details.
As for the first row it labeled with 1 and second with 2, up to 5 row with same id.
So i'm making the table like the picture below. And Now i want to select the table from row to column like in the picture.
i'm making the query like this. I get the result like i want, but the problem is that the query cost like 5/more times the execution plan by just selecting the same table.
My question is, is there a way to make the result like in the picture just by selecting once the table or is there any other way to make like the result with better performance, i hear of using pivot for transposing row to column, but in my case i don't know how to do it. Thanks for reply
this is the execution plan https://www.brentozar.com/pastetheplan/?id=SJGJdaCB7
AND this is the query i used
select * FROM TblKecelakaanBiaya;
with tbl AS (
select *, ROW_NUMBER() OVER(PARTITION by id_kasus ORDER BY id_kasus) rn FROM TblKecelakaanBiaya
)
SELECT A.id_kasus, A.ket_biaya1, A.jlh_biaya1, C.ket_biaya2, C.jlh_biaya2,
D.ket_biaya3, D.jlh_biaya3, E.ket_biaya4, E.jlh_biaya4, F.ket_biaya5, F.jlh_biaya5
FROM (SELECT id_kasus, ket_biaya ket_biaya1, jlh_biaya jlh_biaya1 FROM tbl WHERE rn = 1) A
LEFT JOIN (SELECT id_kasus, ket_biaya ket_biaya2, jlh_biaya jlh_biaya2 FROM tbl WHERE rn = 2) C ON A.id_kasus = C.id_kasus
LEFT JOIN (SELECT id_kasus, ket_biaya ket_biaya3, jlh_biaya jlh_biaya3 FROM tbl WHERE rn = 3) D ON A.id_kasus = D.id_kasus
LEFT JOIN (SELECT id_kasus, ket_biaya ket_biaya4, jlh_biaya jlh_biaya4 FROM tbl WHERE rn = 4) E ON A.id_kasus = E.id_kasus
LEFT JOIN (SELECT id_kasus, ket_biaya ket_biaya5, jlh_biaya jlh_biaya5 FROM tbl WHERE rn = 5) F ON A.id_kasus = F.id_kasus
The execution plan for query 1 is just 1% and the other one is 99%.
Perhaps a Pivot inconcert with a Cross Apply.
Example
Select *
From (
Select id_kasus,item,value
From (Select * ,RN = Row_Number() over (Partition By id_kasus Order By id_kasus) From TblKecekakaanBiaya ) A
Cross Apply (values (concat('jih_biaya',RN),convert(varchar(150),jih_biaya))
,(concat('ket_biaya',RN),ket_biaya)
) b(item,value)
) src
Pivot (max(value) for item in ([ket_biaya1],[jih_biaya1],[ket_biaya2],[jih_biaya2],[ket_biaya3],[jih_biaya3],[ket_biaya4],[jih_biaya4],[ket_biaya5],[jih_biaya5]) ) pvt
Returns

how to set expression variable in query select oracle sql

I have Oracle SQL like these :
SELECT
z."date", z.id_outlet as idOutlet, z.name as outletName, z.matClass, z.targetBulanan, z.targetBulanan/totalVisit as targetAwal,
z.actual,rownumber = tartot + rownumber as targetTotal
FROM (SELECT
b.visit_date as "date", a.id_outlet, max(o.name) as name, max(a.target_sales) as targetBulanan, a.id_material_class as matClass,
max(x.totalVisit) as totalVisit, NVL(SUM(d.billing_value),0) as actual
FROM (
select * from target_bulanan
where deleted = 0 and enabled = 1 and id_salesman = :id_salesman AND id_material_class like :id_material_class AND id_outlet like :id_outlet AND month = TO_NUMBER(TO_CHAR(current_date,'mm')) and year = to_number(TO_CHAR(current_date,'YYYY'))
) a
INNER JOIN outlet o ON o.id_outlet = a.id_outlet
LEFT JOIN visit_plan b ON b.deleted = 0 and a.id_salesman = b.id_salesman AND a.month = TO_NUMBER(TO_CHAR(b.visit_date,'mm')) AND a.year = to_number(TO_CHAR(b.visit_date,'yyyy')) AND a.id_outlet = b.id_outlet
LEFT JOIN so_header c ON SUBSTR(c.id_to,'0',1) = 'TO' AND a.id_salesman = c.id_salesman AND a.id_outlet = c.id_outlet
LEFT JOIN assign_billing d ON c.no_so_sap = d.no_so_sap AND d.billing_date = b.visit_date AND a.id_material_class = (SELECT id_material_class FROM material WHERE id = d.id_material)
LEFt JOIN (SELECT id_salesman, to_char(visit_date,'mm') as month, to_char(visit_date,'yyyy') as year, id_outlet, COUNT(*) as totalVisit FROM visit_plan
WHERE deleted = 0
group by id_salesman, id_outlet,to_char(visit_date,'mm'), to_char(visit_date,'yyyy')) x on
x.id_salesman = a.id_salesman AND x.month = a.month AND x.year = a.year AND x.id_outlet = a.id_outlet
GROUP BY b.visit_date, a.id_outlet, a.id_material_class) z
CROSS JOIN (SELECT 0 as rownumber FROM DUAL ) r
CROSS JOIN (SELECT 0 as tartot FROM DUAL ) t
CROSS JOIN (SELECT '' as mat FROM DUAL ) m
CROSS JOIN (SELECT '' as outlet FROM DUAL ) o
ORDER by outletName, z.matClass, z."date"
I want value of rownumber is formula in my select query but the result is error with this message
ORA-00923: FROM keyword not found where expected
00923. 00000 - "FROM keyword not found where expected"
Anyone can help me ? thanks
Just for enumeration -
replace the line
rownumber = rownumber + 1 AS row_number
with this
rownum AS row_number
rownum is an Oracle inbuilt function that enumerates each record of the result set and with auto increments
As mentioned by Gordon Linoff in his answer, there are further problems in your query.
At the first look (without executing it), I could list the problematic lines -
AND month = TO_NUMBER(TO_CHAR(current_date,'mm'))
AND year = to_number(TO_CHAR(current_date,'YYYY'))
Instead of current_date use sysdate
LEFT JOIN so_header c ON SUBSTR(c.id_to,'0',1) = 'TO'
I guess, you meant to do this -
LEFT JOIN so_header c ON SUBSTR(c.id_to,0,2) = 'TO'
i.e. substring from index 0 upto 2 characters
Plus, no need of those cross joins
THIS ADDRESSES THE ORIGINAL QUESTION.
You may have multiple problems in your query. After all, the best way to debug and to write queries is to start simple and gradually add complexity.
But, you do have an obvious error. In your outermost select:
SELECT z."date", z.id_outlet as idOutlet, z.name as outletName,
z.matClass, z.targetBulanan, z.targetBulanan/totalVisit as targetAwal,
z.actual,
rownumber = rownumber + 1 as row_number
The = is not Oracle syntax -- it looks like a SQL Server extension for naming a column or a MySQL use of variables.
I suspect that you want to enumerate the rows. If so, one syntax is row_number():
SELECT z."date", z.id_outlet as idOutlet, z.name as outletName,
z.matClass, z.targetBulanan, z.targetBulanan/totalVisit as targetAwal,
z.actual,
row_number() over (order by outletName, z.matClass, z."date") as row_number
In Oracle, you could also do:
rownum as row_number

Use having and order by in sql server

I would want to select the elements of the column sanciones.matricula_vehiculo that appear more than once, the next code shows all the elements; but would lack a restricion similiar to > 1
SELECT
vehiculos.marca_vehiculo,
sanciones.matricula_vehiculo,
vehiculos.modelo_vehiculo
FROM vehiculos
INNER JOIN sanciones
ON vehiculos.matricula_vehiculo=sanciones.matricula_vehiculo
ORDER BY vehiculos.marca_vehiculo;
I think this does what you want:
SELECT marca_vehiculo, matricula_vehiculo, modelo_vehiculo
FROM (SELECT v.marca_vehiculo, s.matricula_vehiculo, v.modelo_vehiculo,
COUNT(*) OVER (PARTITION BY s.matricula_vehiculo) as cnt
FROM vehiculos v INNER JOIN
sanciones s
ON v.matricula_vehiculo = s.matricula_vehiculo
) vs
WHERE cnt > 1
ORDER BY marca_vehiculo;
SELECT matricula_vehiculo
FROM vehiculos
INNER JOIN sanciones ON vehiculos.matricula_vehiculo=sanciones.matricula_vehiculo
GROUP BY matricula_vehiculo
HAVING count(*) > 1
ORDER BY vehiculos.marca_vehiculo
This will work too, and you will not have the overhead of a JOIN by using EXISTS:
SELECT
vehiculos.marca_vehiculo,
vehiculos.matricula_vehiculo,
vehiculos.modelo_vehiculo
FROM vehiculos v
WHERE EXISTS ( SELECT *
FROM sanciones s
WHERE s.matricula_vehiculo = v.matricula_vehiculo
HAVING COUNT(*) > 1 )
ORDER BY vehiculos.marca_vehiculo;

Multiple MAX values select using inner join

I have query that work for me only when values in the StakeValue don't repeat.
Basically, I need to select maximum values from SI_STAKES table with their relations from two other tables grouped by internal type.
SELECT a.StakeValue, b.[StakeName], c.[ProviderName]
FROM SI_STAKES AS a
INNER JOIN SI_STAKESTYPES AS b ON a.[StakeTypeID] = b.[ID]
INNER JOIN SI_PROVIDERS AS c ON a.[ProviderID] = c.[ID] WHERE a.[EventID]=6
AND a.[StakeGroupTypeID]=1
AND a.StakeValue IN
(SELECT MAX(d.StakeValue) FROM SI_STAKES AS d
WHERE d.[EventID]=a.[EventID] AND d.[StakeGroupTypeID]=a.[StakeGroupTypeID]
GROUP BY d.[StakeTypeID])
ORDER BY b.[StakeName], a.[StakeValue] DESC
Results for example must be:
[ID] [MaxValue] [StakeTypeID] [ProviderName]
1 1,5 6 provider1
2 3,75 7 provider2
3 7,6 8 provider3
Thank you for your help
There are two problems to solve here.
1) Finding the max values per type. This will get the Max value per StakeType and make sure that we do the exercise only for the wanted events and group type.
SELECT StakeGroupTypeID, EventID, StakeTypeID, MAX(StakeValue) AS MaxStakeValue
FROM SI_STAKES
WHERE Stake.[EventID]=6
AND Stake.[StakeGroupTypeID]=1
GROUP BY StakeGroupTypeID, EventID, StakeTypeID
2) Then we need to get only one return back for that value since it may be present more then once.
Using the Max Value, we must find a unique row for each I usually do this by getting the Max ID is has the added advantage of getting me the most recent entry.
SELECT MAX(SMaxID.ID) AS ID
FROM SI_STAKES AS SMaxID
INNER JOIN (
SELECT StakeGroupTypeID, EventID, StakeTypeID, MAX(StakeValue) AS MaxStakeValue
FROM SI_STAKES
WHERE Stake.[EventID]=6
AND Stake.[StakeGroupTypeID]=1
GROUP BY StakeGroupTypeID, EventID, StakeTypeID
) AS SMaxVal ON SMaxID.StakeTypeID = SMaxVal.StakeTypeID
AND SMaxID.StakeValue = SMaxVal.MaxStakeValue
AND SMaxID.EventID = SMaxVal.EventID
AND SMaxID.StakeGroupTypeID = SMaxVal.StakeGroupTypeID
3) Now that we have the ID's of the rows that we want, we can just get that information.
SELECT Stakes.ID, Stakes.StakeValue, SType.StakeName, SProv.ProviderName
FROM SI_STAKES AS Stakes
INNER JOIN SI_STAKESTYPES AS SType ON Stake.[StakeTypeID] = SType.[ID]
INNER JOIN SI_PROVIDERS AS SProv ON Stake.[ProviderID] = SProv.[ID]
WHERE Stake.ID IN (
SELECT MAX(SMaxID.ID) AS ID
FROM SI_STAKES AS SMaxID
INNER JOIN (
SELECT StakeGroupTypeID, EventID, StakeTypeID, MAX(StakeValue) AS MaxStakeValue
FROM SI_STAKES
WHERE Stake.[EventID]=6
AND Stake.[StakeGroupTypeID]=1
GROUP BY StakeGroupTypeID, EventID, StakeTypeID
) AS SMaxVal ON SMaxID.StakeTypeID = SMaxVal.StakeTypeID
AND SMaxID.StakeValue = SMaxVal.MaxStakeValue
AND SMaxID.EventID = SMaxVal.EventID
AND SMaxID.StakeGroupTypeID = SMaxVal.StakeGroupTypeID
)
You can use the over clause since you're using T-SQL (hopefully 2005+):
select distinct
a.stakevalue,
max(a.stakevalue) over (partition by a.staketypeid) as maxvalue,
b.staketypeid,
c.providername
from
si_stakes a
inner join si_stakestypes b on
a.staketypeid = b.id
inner join si_providers c on
a.providerid = c.id
where
a.eventid = 6
and a.stakegrouptypeid = 1
Essentially, this will find the max a.stakevalue for each a.staketypeid. Using a distinct will return one and only one row. Now, if you wanted to include the min a.id along with it, you could use row_number to accomplish this:
select
s.id,
s.maxvalue,
s.staketypeid,
s.providername
from (
select
row_number() over (order by a.stakevalue desc
partition by a.staketypeid) as rownum,
a.id,
a.stakevalue as maxvalue,
b.staketypeid,
c.providername
from
si_stakes a
inner join si_stakestypes b on
a.staketypeid = b.id
inner join si_providers c on
a.providerid = c.id
where
a.eventid = 6
and a.stakegrouptypeid = 1
) s
where
s.rownum = 1