SQL Server : query grouping - sql

I have some queries in SQL Server. I have two tables
keyword_text
Keyword_relate
Columns in keyword_text:
key_id
keywords
Columns in keyword_relate:
key_id
product_id
score
status
Sample data for keyword_text:
----|----------
1 | Pencil
2 | Pen
3 | Books
Sample data for keyword_relate:
----------------------------
Sno| Product | SCore|status
---------------------------
1 | 124 | 2 | 1
1 | 125 | 3 | 1
2 | 124 | 3 | 1
2 | 125 | 2 | 1
From this I want to get the product_id, grouped by keywords and which have maximum score

Presuming that key_id of first table is Sno in second table. You can use ROW_NUMBER:
WITH CTE AS
(
SELECT Product AS ProductID, Score As MaxScore,
RN = ROW_NUMBER() OVER (PARTITION BY kt.key_id ORDER BY Score DESC)
FROM keyword_text kt INNER JOIN keyword_relate kr
ON kt.key_id = kr.Sno
)
SELECT ProductID, MaxScore
FROM CTE
WHERE RN = 1

Related

SQL - Return records with highest version for each quotation ID

If I have a table with three columns like below
CREATE TABLE QUOTATIONS
(
ID INT NOT NULL,
VERSION INT NOT NULL,
PRICE FLOAT NOT NULL
);
In addition to this, lets say that the table consists of the follow records:
ID | VERSION | PRICE
----+-----------+--------
1 | 1 | 50
1 | 2 | 40
1 | 3 | 30
2 | 1 | 100
2 | 2 | 80
3 | 1 | 50
Is there any single SQL query that can be run and return the rows of all quotations with the highest version only?
The results should be like follow:
ID | VERSION | PRICE
----+-----------+--------
1 | 3 | 30
2 | 2 | 80
3 | 1 | 50
I like this method which uses no subqueries:
select top (1) with ties q.*
from quotations q
order by row_number() over (partition by id order by version desc);
Basically, the row_number() assigns "1" to the highest version for each id. The top (1) with ties returns all the 1s.
SELECT id,version,price
FROM tableName t
JOIN (SELECT id,MAX(version) AS version
FROM tableName
GROUP BY id) AS q1 ON q1.id = t.id AND q1.version = t.version

Sum across columns and rows

Consider a table like this
table
+--------+---------+-----------+---------+-----------+
| BookId | ItemId1 | Quantity1 | ItemId2 | Quantity2 |
+--------+---------+-----------+---------+-----------+
| 1 | 1 | 2 | 2 | 1 |
| 1 | 3 | 1 | 2 | 1 |
| 2 | 1 | 1 | 2 | 1 |
+--------+---------+-----------+---------+-----------+
Now I want to get the sum of columns quantity for each item grouped by book. How can I take the sum across different columns then? right now I use an awkward solution like building a temporary table and then querying this one, but it must be possible in a more elegant way!?
select
BookId 'BookId',
ItemId1 'ItemId',
Quantity1 'Quantity'
into #temptab
from table
union all
select
BookId,
ItemId2,
Quantity2
from table
and after that
select
BookId,
ItemId,
sum(Quantity)
from #temptab
group by ItemId, BookId
How can I get rid of this intermediate step?
Desired output:
+--------+--------+----------+
| BookId | ItemId | Quantity |
+--------+--------+----------+
| 1 | 1 | 2 |
| 1 | 3 | 1 |
| 1 | 2 | 2 |
| 2 | 1 | 1 |
| 2 | 2 | 1 |
+--------+--------+----------+
Use cross apply with table valued constructor to unpivot the data then find sum per bookid and item.
This will avoid your intermediate step
SELECT BookId,
item,
Sum(quantity)
FROM Youratble
CROSS apply (VALUES(Quantity1,ItemId1),
(Quantity2,ItemId2))cs(quantity, item)
GROUP BY BookId,
item
As mentioned by Mikael Eriksson for sql server 2005 use this
SELECT BookId,
item,
Sum(quantity)
FROM Youratble
cross apply
(select Quantity1, ItemId1
union all
select Quantity2, ItemId2) as cs(quantity, item)
GROUP BY BookId,
item
Based on what you've done, you can do it in one single step using subqueries:
select
combined.BookId,
combined.ItemId,
sum(combined.Quantity)
from
(select
BookId 'BookId',
ItemId1 'ItemId',
Quantity1 'Quantity'
from tableName
union all
select
BookId,
ItemId2,
Quantity2
from tableName) as combined
group by combined.ItemId, combined.BookId

Sql two table query most duplicated foreign key

I got those two tables sport and student:
First table sport:
|idsport | name |
_______________________
| 1 | bobsled |
| 2 | skating |
| 3 | boarding |
| 4 | iceskating |
| 5 | skiing |
Second table student:
foreign key
|idstudent | name | sport_idsport
__________________________________________
| 1 | john | 3 |
| 2 | pauly | 2 |
| 3 | max | 1 |
| 4 | jane | 2 |
| 5 | nico | 5 |
so far i did this it output which number is mostly inserted, but cant get it to work
with two tables
SELECT sport_idsport
FROM (SELECT sport_idsport FROM student GROUP BY sport_idsport ORDER BY COUNT(*) desc)
WHERE ROWNUM<=1;
I need to output name of most popular sport, in that case it would be skating.
I use oracle sql.
with counter as (
Select sport_idsport,
count(*) as cnt,
dense_rank() over (order by count(*) desc) as rn
from student
group by sport_idsport
)
select s.*, c.cnt
from sport s
join counter c on c.sport_idsport = s.idsport and c.rn = 1;
SQLFiddle example: http://sqlfiddle.com/#!4/b76e21/1
select cnt, sport_idsport from (
select count(*) cnt, sport_idsport
from student
group by sport_idsport
order by count(*) desc
)
where rownum = 1

Inconsistent Transpose

Given a table A has the following data:
+----------+-------+
| Supplier | buyer |
+----------+-------+
| A | 1 |
| A | 2 |
| B | 3 |
| B | 4 |
| B | 5 |
+----------+-------+
My question is, can I transpose the second column so the resultant table will be like:
+----------+--------+--------+--------+
| Supplier | buyer1 | buyer2 | buyer3 |
+----------+--------+--------+--------+
| A | 1 | 2 | |
| B | 3 | 4 | 5 |
+----------+--------+--------+--------+
Assuming the maximum number of buyers is known as three.
You could use a common table expression to give each buyer an order within the supplier, and then just do a regular case to put them in columns;
WITH cte AS (
SELECT supplier, buyer,
ROW_NUMBER() OVER (PARTITION BY supplier ORDER BY buyer) rn
FROM Table1
)
SELECT supplier,
MAX(CASE WHEN rn=1 THEN buyer END) buyer1,
MAX(CASE WHEN rn=2 THEN buyer END) buyer2,
MAX(CASE WHEN rn=3 THEN buyer END) buyer3
FROM cte
GROUP BY supplier;
An SQLfiddle to test with.
You may consider using PIVOT clause:
select *
from (
select supplier, buyer, row_number() over (partition by supplier order by buyer) as seq
from a
)
pivot (max(buyer) for seq in (1 as buyer1, 2 as buyer2, 3 as buyer3));
SQLFiddle here.

select top 1 with max 2 fields

I have this table :
+------+-------+------------------------------------+
| id | rev | class |
+------+-------+------------------------------------+
| 1 | 10 | 2 |
| 1 | 10 | 5 |
| 2 | 40 | 6 |
| 2 | 50 | 6 |
| 2 | 52 | 1 |
| 3 | 33 | 3 |
| 3 | 63 | 5 |
+------+-------+------------------------------------+
I only need the rows where rev AND then class columns have max value.
+------+-------+------------------------------------+
| id | rev | class |
+------+-------+------------------------------------+
| 1 | 10 | 5 |
| 2 | 52 | 1 |
| 3 | 63 | 5 |
+------+-------+------------------------------------+
Query cost is important for me.
Just the rows that satisfy the condition that it has both max values?
Here's an SQL Fiddle;
SELECT h.id, h.rev, h.class
FROM ( SELECT id,
MAX( rev ) rev,
MAX( class ) class
FROM Herp
GROUP BY id ) derp
INNER JOIN Herp h
ON h.rev = derp.rev
AND h.class = derp.class;
The fastest way might be to have an index on t(id, rev) and t(id, class) and then do:
select t.*
from table t
where not exists (select 1
from table t2
where t2.id = t.id and t2.rev > t.rev
) and
not exists (select 1
from table t2
where t2.id = t.id and t2.class > t.class
);
SQL Server is pretty smart in terms of optimization, so the aggregation approach might be just as good. However, in terms of performance, this is just a bunch of index lookups.
Here is a SQL 2012 example. Very straight forward with the implied table and the PARTITION function.
Basically, with each ID as a partition/group, sort the values of the other fields in a descending order assigning each one an incrementing RowId, then only take the first one.
select id, rev, [class]
from
(
SELECT id, rev, [class],
ROW_NUMBER() OVER(PARTITION BY id ORDER BY rev DESC, [class] desc) AS RowId
FROM sample
) t
where RowId = 1
Here is the SQL Fiddle
Keep in mind, this works with the criteria in the example dataset, and not the MAX of two fields as stated in the question's title.
I guess you mean: the max of rev and the max of class. If not, please clarify what to do when there is no row where both fields have the highest value.
select id
, max(rev)
, max(class)
from table
group
by id
If you mean total value of rev and class use this:
select id
, max
, rev
from table
where id in
( select id
, max(rev + class)
from table
group
by id
)