I have 3 tables in my database which is
table 1 (users)
userid(PK)
EmployeeName
table 2 (SubDept)
SubDeptID(PK)
Department
table 3 (SubDeptTransfer)
TransferID(PK)
userid(FK)
SubDeptID(FK)
here is my example table for Table 3
what i wanted to do is to be able to print the SubDeptID of user 100. The problem is since there are two userid of 100 its printing both. the mission is to be able to print only one data with a latter TransferID. What could be the best select statement for the problem?
The best way to do this is using the window function row_number():
select transferId, userId, subDeptId
from (select t.*,
row_number() over (partition by userid order by TransferId desc) as seqnum
from t
) t
where seqnum = 1
I would do it like so:
SELECT subDeptId FROM SubDeptTransfer WHERE userId = 100 ORDER BY transferId DESC LIMIT 1
Related
I have 3 different recommendation model that gives me the output in three different tables.
Recommendation 1 : In a ideal situation, I want to take top 2 recommendation per user from this table ordered by ProductRecommendation ascending.
Recommendation 2 : In a ideal situation, I want to take top 3 recommendation per user from this table based on top score.
Recommendation 3 : In a ideal situation, take remaining recommendation from this table to add up to 5 recommendation per user
In the end, I want to see a final output which is a merge of all the recommendation into one which would look like this.
I want to take top 5 recommendation across 3 different tables. FYI, not all the user id can appear in all the tables. Ideally, I want to take TOP 2 from recommendation 1, TOP 3 from recommendation 2. Recommendation 3 is just there so that if there are not enough recommendation from the first two table then recommendation 3 will compensate so at the end I will get 5 results per userID. I don't need to refer to recommendation 3 if I can get 5 recommendation (2 from recommendation 1 and 3 from recommendation 2). when the recommendation 1 has < 2 recommendations per user then I want to get the remaining of the recommendation from recommendation 2. For example, when there is 1 recommendation in Recommendtiaon1 then get 4 recommendation from Recommendation2. Alternatively, if there are 0 recommendation in Recommendation1 then get 5 recommendation from Recommendation2. If Recommednation1 and Recommendation2 doesn't add up to 5 that's when I need to refer to recommendation3. I need to do this in big query SQL. Can you please help?
Thanks for your help.
Consider below approach
with output1 as (
select *, null as Score, row_number() over win pos
from Recommendation1
where true
qualify row_number() over win <= 2
window win as (partition by UserID order by ProductRecommendation)
), output2 as (
select *, 2 + row_number() over win pos
from Recommendation2
where not (UserID, ProductRecommendation) in (select as struct UserID, ProductRecommendation from output1)
qualify row_number() over win <= 5
window win as (partition by UserID order by Score desc)
), output3 as (
select *, 7 + row_number() over win pos
from Recommendation3
where not (UserID, ProductRecommendation) in (select as struct UserID, ProductRecommendation from output1)
and not (UserID, ProductRecommendation) in (select as struct UserID, ProductRecommendation from output2)
qualify row_number() over win <= 5
window win as (partition by UserID order by Score desc)
)
select * except(pos) from (
select * from output1 union all
select * from output2 union all
select * from output3
)
where true
qualify row_number() over win <=5
window win as (partition by UserID order by pos)
# order by UserID, pos
if applied to sample data in your question - the output is
Your description is a bit unclear. The following takes 2 rows from the first table for each user, 3 from the second, and additional rows from the third. The outer query then ensures that there are 5 rows (if available) for each user:
select r.*
from ((select userid, recommendation, 1 as which
from recommendation1
where 1=1
qualify row_number() over (partition by userid order by recommendation) <= 2
) union all
(select userid, recommendation, 2 as which
from recommendation2
where 1=1
qualify row_number() over (partition by userid order by score desc) <= 3
) union all
(select userid, recommendation, 3 as which
from recommendation3
)
) r
where 1=1
qualify row_number() over (partition by userid order by which) <= 5;
I have table with 3 Columns (ID, CARDNO, CONTACTNO)
Each cardno have multiple contactno
ID CARDNO CONTACTNO
1 1234567895412 32225465987
2 1234567895412 65554789654
3 1234567895412 24445698741
4 1234567895412 24445698745
5 1234567895412 45556987123
I want only select 3 random contactno against 1 card
Well you have several options, but I think that the easiest one is:
SELECT *
FROM contact_data -- your table name
ORDER BY dbms_random.random
FETCH FIRST 3 ROWS ONLY;
You can use the ROW_NUMBER analytical function as follows:
SELECT ID, CARDNO, CONTACTNO FROM
(SELECT ID, CARDNO, CONTACTNO,
ROW_NUMBER() OVER (PARTITION BY CARDNO ORDER BY 1) AS RN
FROM YOUR_TABLE)
WHERE RN <= 3;
If you really need randomness then you can replace ORDER BY 1 with ORDER BY dbms_random.value(0,1) in the OVER clause of the ROW_NUMBER.
May be you can just try something like this
SELECT * FROM TABLE WHERE CARDNO = '1234567895412' ORDER BY RAND() LIMIT 3;
replace TABLE with your table name,
If you really want random then use row_number() with a dbms_random function:
select t.*
from (select t.*,
row_number() over (partition by cardno order by dbms_random.value) as seqnum
from t
) t
where seqnum <= 3;
Colloquially, though, people often use "random" but not in the technical sense. If that is the case, you can replace the order by with whatever makes sense -- including dropping it altogether.
I have a database table which looks like this.
id account_id action time_point
3 234 delete 100
1 656 create 600
1 4435 update 900
3 645 create 50
I need to group this table by id and select particular row where time_point has a largest value.
Result table should look like this:
id account_id action time_point
3 234 delete 100
1 4435 update 900
Thanks for help,
qwew
In Postgres, I would recommend distinct on to solve this top 1 per group problem:
select distinct on (id) *
from mytable
order by id, time_point desc
However, this does not allow possible to ties. If so, rank() is a better solution:
select *
from (
select t.*, rank() over(partition by id order by time_point desc) rn
from mytable t
) t
where rn = 1
Or, if you are running Postgres 13:
select *
from mytable t
order by rank() over(partition by id order by time_point desc)
fetch first row with ties
check this.
select * from x
where exists (
select 1 from x xin
where xin.id = x.id
having max(time_point) = time_point
);
OK, I'm a little outta practice on SQL Queries here, I have a table with thousands of entries.
Each row has a unique Id but there is a column named EquipmentId which is not unique and would be present in several rows. I want to return 3 rows for every EquipmentId and if there is less the than 3 entries for an EquipmentID I want those too. ..... make sense ? thanks in advance.
Use ROW_NUMBER() + CTE
;WITH CTE AS(
SELECT *,
ROW_NUMBER() OVER ( PARTITION BY EquipmentId ORDER BY ID ) RN
FROM TableName
)
SELECT *
FROM CTE
WHERE RN <= 3
ORDER BY EquipmentId
Using subqueries you can do it like this:
SELECT *
FROM
(SELECT *, Rank()
OVER
(PARTITION BY equipmentid
ORDER BY ID) Rank
FROM stack) AS a
WHERE
rn <= 3
I have a user table which contains among others money, level and ranking.
Id | money| ranking| level
---------------------------
1 |30000| 1 1
2 |20000| 2 3
3 |10000| 3 2
4 |50000| 4 2
I want to update the ranking field based on user level (first filter) and money.
That is a user in higher level will always be ranked higher.
That is i want the table after the update like this:
Id | money| ranking| level
---------------------------
1 |30000| 4 1
2 |20000| 1 3
3 |10000| 3 2
4 |50000| 2 2
Thanks!
As a side note, I would NOT store this field within the database - storing values that are dependent on other records in the table make maintenance much more difficult.
Here's a query that would work as a view or within a stored procedure:
SELECT
ID,
[money],
ROW_NUMBER() OVER (order by [level] desc, [money] desc) AS [ranking],
[level]
FROM myTable
If you REALLY wanted to update the table just make the query a subquery to an update:
UPDATE m1
SET ranking = m2.ranking
FROM myTable m1
INNER JOIN
(SELECT
ID,
ROW_NUMBER() OVER (order by [level] desc, [money] desc) ranking
FROM myTable) m2
ON m1.ID = m2.ID
If you simply want to select then here is the query :
select *, dense_rank() over (order by level desc, mony desc) as newranking from YourTable
and if you want to update then :
;with cte_toupdate (ranking, newranking)
as (
select ranking, dense_rank() over (order by level desc, mony desc) as newranking from YourTable
)
Update cte_toupdate set ranking = newranking
select * from YourTable
check here : http://sqlfiddle.com/#!3/8d6d3/10
Note : if you want unique ranks then use Row_Number() instead of dense_rank().
CREATE TABLE #tt
(
id INT,
mony INT,
ranking INT,
levell INT
)
INSERT INTO #tt
VALUES (1,30000,1,1),
(2,20000,2,3),
(3,10000,3,2),
(4,50000,4,2)
UPDATE a
SET ranking = rnk
FROM #tt a
JOIN (SELECT Row_number()
OVER (
ORDER BY levell DESC, mony DESC) AS rnk,
*
FROM #tt) b
ON a.levell = b.levell
AND a.mony = b.mony