Get every second row as a result table in t-sql - sql

I'm looking for a t-sql script that returns a list, that shows every second value from a grouping from Table1.
For example I have the following data (Table1) and want the desired result-list:
Table1:
Customer Quantity
A 5
A 8 (*)
B 3
B 5 (*)
B 11
C 7
D 4
D 23 (*)
Desired retult-list:
Customer Quantity
A 8
B 5
D 23
I think about doing something something with 'select distinct and left outer join', but I can't get it to work. Possibly I need an row numbering, but can't figure out how to do it. Anyone can help me?
Beneath is the script I used to make and fill Table1:
CREATE TABLE Table1
(Customer nvarchar(1) NULL,
Quantity int NOT NULL);
INSERT INTO Table1(Customer,Quantity)
VALUES
('A',5),
('A',8),
('B',3),
('B',5),
('B',11),
('C',7),
('D',4),
('D',23);

This can be done quite easily using the row_number window function:
SELECT customer, quantity
FROM (SELECT customer, quantity,
ROW_NUMBER() OVER (PARTITION BY customer
ORDER BY quantity ASC) AS rn
FROM table1) t
WHERE rn = 2

You can use ROW_NUMBER and a CTE:
WITH data AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Customer ORDER BY Quantity) rn
FROM #Table1
)
SELECT Customer, Quantity
FROM data
WHERE rn = 2
How it works:
Using ROW_NUMBER() will assign a sequential number to each row based on what's specified in OVER (). In OVER i specify to PARTITION the rows on customer, that means each group of data on same customer will be numberered separately. Then ORDER BY Quantity mean it should order the data based on quantity for each customer - so i can get the 2nd row for each customer ordered by quantity.

Related

Second method for getting output using SQL query in ORACLE

I have a table which has data as:
My expected output is:
I got my expected output as using rownum:
SELECT ID,PRICE FROM OT.TEST1 WHERE ROWNUM<3;
It's working finesince i have inserted the data serially as the output is coming with rownum ,but what,if the data were inserted as random below,my rownum will not work.Is there any new method?
ID PRice
3 300
3 600
8 600
2 600
You could use ROW_NUMBER() here:
WITH cte AS (
SELECT t.*, ROW_NUMBER() OVER (ORDER BY PRICE, ID) rn
FROM OT.TEST1 t
)
SELECT ID, PRICE
FROM cte
WHERE rn <= 2;
Here we are assigning a row number over the entire table ordered first by price ascending, and then by ID. Since three records are tied for a price of 600, the record with the lowest ID would happen to be returned here.

SQL Server 2008 - ROWNUMBER OVER - filtering the result

I have the following SQL which works and returns products with duplicate names and the rownum column is a count of how many times that name appears.
Adding where rownum > 1 at the end gives me the duplicates only.
SELECT *
FROM
(SELECT
id, productname,
ROW_NUMBER() OVER (PARTITION BY productname
ORDER BY productname) Rownum
FROM products
GROUP BY id, productname) result
REQUIREMENT
I need to produce a list of products where if the rownum column has a value greater than one, I want to see all the rows pertaining to that product grouped by the name column.
If the rownum value for a product is 1 only, and no value greater than one (so no duplicate) I don't want to see that row.
So for example if "Blue umbrella" appears three times, I want to see the result for this product as:
ID Name Rownum
35 Blue umbrella 1
41 Blue umbrella 2
90 Blue umbrella 3
How would I go about achieving this please?
Change the Row_NUmber Over to Count(1) Over and select where the count is greater than 1 and remove the group by
SELECT * from (Select id,productname,
Count(1) OVER(Partition By productname ORDER by productname) Rownum
FROM products
) result
WHERE Rownum > 1

Is it possible to create and use window function in the same query?

I'm using PostgreSQL and I have the following situation:
table of Sales (short version):
itemid quantity
5 10
5 12
6 1
table of stock (short version):
itemid stock
5 30
6 1
I have a complex query that also needs to present in one of it's columns the SUM of each itemid.
So it's going to be:
Select other things,itemid,stock, SUM (quantity) OVER (PARTITION BY itemid) AS total_sales
from .....
sales
stock
This query is OK. however this query will present:
itemid stock total_sales
5 30 22
6 1 1
But I don't need to see itemid=6 because the whole stock was sold. meaning that I need a WHERE condition like:
WHERE total_sales<stock
but I can't do that as the total_sales is created after the WHERE is done.
Is there a way to solve this without surrounding the whole query with another one? I'm trying to avoid it if I can.
You can use a subquery or CTE:
select s.*
from (Select other things,itemid,stock,
SUM(quantity) OVER (PARTITION BY itemid) AS total_sales
from .....
) s
where total_sales < stock;
You cannot use table aliases defined in a SELECT in the SELECT, WHERE, or FROM clauses for that SELECT. However, a subquery or CTE gets around this restriction.
You can also use an inner select in your WHERE statement like this:
SELECT *, SUM (quantity) OVER (PARTITION BY itemid) AS total_sales
FROM t
WHERE quantity <> (SELECT SUM(quantity) FROM t ti WHERE t.itemid = ti.itemid);

select multiple records based on order by

i have a table with a bunch of customer IDs. in a customer table is also these IDs but each id can be on multiple records for the same customer. i want to select the most recently used record which i can get by doing order by <my_field> desc
say i have 100 customer IDs in this table and in the customers table there is 120 records with these IDs (some are duplicates). how can i apply my order by condition to only get the most recent matching records?
dbms is sql server 2000.
table is basically like this:
loc_nbr and cust_nbr are primary keys
a customer shops at location 1. they get assigned loc_nbr = 1 and cust_nbr = 1
then a customer_id of 1.
they shop again but this time at location 2. so they get assigned loc_nbr = 2 and cust_Nbr = 1. then the same customer_id of 1 based on their other attributes like name and address.
because they shopped at location 2 AFTER location 1, it will have a more recent rec_alt_ts value, which is the record i would want to retrieve.
You want to use the ROW_NUMBER() function with a Common Table Expression (CTE).
Here's a basic example. You should be able to use a similar query with your data.
;WITH TheLatest AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY group-by-fields ORDER BY sorting-fields) AS ItemCount
FROM TheTable
)
SELECT *
FROM TheLatest
WHERE ItemCount = 1
UPDATE: I just noticed that this was tagged with sql-server-2000. This will only work on SQL Server 2005 and later.
Since you didn't give real table and field names, this is just psuedo code for a solution.
select *
from customer_table t2
inner join location_table t1
on t1.some_key = t2.some_key
where t1.LocationKey = (select top 1 (LocationKey) as LatestLocationKey from location_table where cust_id = t1.cust_id order by some_field)
Use an aggregate function in the query to group by customer IDs:
SELECT cust_Nbr, MAX(rec_alt_ts) AS most_recent_transaction, other_fields
FROM tableName
GROUP BY cust_Nbr, other_fields
ORDER BY cust_Nbr DESC;
This assumes that rec_alt_ts increases every time, thus the max entry for that cust_Nbr would be the most recent entry.
By using time and date we can take out the recent detail for the customer.
use the column from where you take out the date and the time for the customer.
eg:
SQL> select ename , to_date(hiredate,'dd-mm-yyyy hh24:mi:ss') from emp order by to_date(hiredate,'dd-mm-yyyy hh24:mi:ss');

How do I generate line item numbers in an oracle sql query

This seems like it should be a classic invoice-item problem that has already been solved but maybe I'm not using the right words in my searches.
I am running a query like this (this is just a simple example, my real query is much more complex but it returns the same results):
select invoice.inv_num, item.name, item.qty
from invoice invoice, item
where invoice.inv_num = item.inv_num
order by invoice.inv_num
I need to generate an item number column that increments for each item but starts over at 1 for each new invoice number. So, for example, I need the end result to look something like this:
inv_num item_num name qty
------- -------- ------------- ---
111 1 red widgets 10
111 2 blue widgets 5
222 1 green_widgets 7
222 2 red_widgets 16
222 3 black_widgets 10
333 1 blue_widgets 8
333 2 red_widgets 12
We are still using Oracle 9i in case that makes a difference.
You can use the oracle rank or row_number analytic functions (depending on how you want to deal with duplicates/euqally ranked items).
Here's how you would add a 4th column item_number to your query :
select invoice.inv_num, item.name, item.qty ,
row_number() over (partition by inv_num order by qty desc) item_num
from invoice invoice, item
where invoice.inv_num = item.inv_num
order by invoice.inv_num
The counter resets at each new invoice number becuase of the
partition by clause.
Within an invoice, the rank/item number is decided by qty (highest
to lowest).
In the above query, rank or row_number will give the same result with your data. But if there are multiple items with the same quantity in an invoice (10 red, 10 blue widgets), rank will give you equal item numbers, so in this case row_number is appropriate.
http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions001.htm#i81407
select invoice.inv_num,
item.name,
item.qty,
row_number() OVER(PARTITION BY invoice.inv_num order by item.qty desc) as item_num
from invoice invoice, item
where invoice.inv_num = item.inv_num
order by invoice.inv_num
row_number() generates Number starting with 1.. And we restart the sequence for every *INV_NUM* using PARTITION BY clause. And ordering of numbering with qty.
Assuming the item name of an invoice is unique you can use rank() aggregate function:
create table TESTTABLE(inv_num NUMBER,iname VARCHAR2(100 CHAR),qty NUMBER);
insert into TESTTABLE values(111,'red widgets',10);
insert into TESTTABLE values(111,'blue widgets',5);
insert into TESTTABLE values(222,'green_widgets',7);
insert into TESTTABLE values(222,'red_widgets',16);
insert into TESTTABLE values(222,'black_widgets',10);
insert into TESTTABLE values(333,'blue_widgets',8);
insert into TESTTABLE values(333,'red_widgets',12);
commit;
select inv_num, iname, qty ,
rank() over (PARTITION BY inv_num ORDER BY iname) as item_num
from TESTTABLE
order by inv_num;