Specific Ordering in SQL - sql

I have a SQL Server 2008 database. In this database, I have a result set that looks like the following:
ID Name Department LastOrderDate
-- ---- ---------- -------------
1 Golf Balls Sports 01/01/2015
2 Compact Disc Electronics 02/01/2015
3 Tires Automotive 01/15/2015
4 T-Shirt Clothing 01/10/2015
5 DVD Electronics 01/07/2015
6 Tennis Balls Sports 01/09/2015
7 Sweatshirt Clothing 01/04/2015
...
For some reason, my users want to get the results ordered by department, then last order date. However, not by department name. Instead, the departments will be in a specific order. For example, they want to see the results ordered by Electronics, Automotive, Sports, then Clothing. To throw another kink in works, I cannot update the table schema.
Is there a way to do this with a SQL Query? If so, how? Currently, I'm stuck at
SELECT *
FROM
vOrders o
ORDER BY
o.LastOrderDate
Thank you!

You can use case expression ;
order by case when department = 'Electronics' then 1
when department = 'Automotive' then 2
when department = 'Sports' then 3
when department = 'Clothing' then 4
else 5 end

create a table for the departments that has the name (or better id) of the department and the display order. then join to that table and order by the display order column.
alternatively you can do a order by case:
ORDER BY CASE WHEN Department = 'Electronics' THEN 1
WHEN Department = 'Automotive' THEN 2
...
END
(that is not recommended for larger tables)

Here solution with CTE
with c (iOrder, dept)
as (
Select 1, 'Electronics'
Union
Select 2, 'Automotive'
Union
Select 3, 'Sports'
Union
Select 4, 'Clothing'
)
Select * from c
SELECT o.*
FROM
vOrders o join c
on c.dept = o.Department
ORDER BY
c.iOrder

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)

Reconciliation Automation Query

I have one database and time to time i change some part of query as per requirement.
i want to keep record of results of both before and after result of these queries in one table and want to show queries which generate difference.
For Example,
Consider following table
emp_id country salary
---------------------
1 usa 1000
2 uk 2500
3 uk 1200
4 usa 3500
5 usa 4000
6 uk 1100
Now, my before query is :
Before Query:
select count(emp_id) as count,country from table where salary>2000 group by country;
Before Result:
count country
2 usa
1 uk
After Query:
select count(emp_id) as count,country from table where salary<2000 group by country;
After Query Result:
count country
2 uk
1 usa
My Final Result or Table I want is:
column 1 | column 2 | column 3 | column 4 |
2 usa 2 uk
1 uk 1 usa
...... but if query results are same than it shouldn't show in this table.
Thanks in advance.
I believe that you can use the same approach as here.
select t1.*, t2.* -- if you need specific columns without rn than you have to list them here
from
(
select t.*, row_number() over (order by count) rn
from
(
-- query #1
select count(emp_id) as count,country from table where salary>2000 group by country;
) t
) t1
full join
(
select t.*, row_number() over (order by count) rn
from
(
-- query #2
select count(emp_id) as count,country from table where salary<2000 group by country;
) t
) t2 on t1.rn = t2.rn

Concatenating data from one row into the results from another

I have a SQL Server database of orders I'm struggling with. For a normal order a single table provides the following results:
Orders:
ID Customer Shipdate Order ID
-----------------------------------------------------------------
1 Tom 2015-01-01 100
2 Bob 2014-03-20 200
At some point they needed orders that were placed by more than one customer. So they created a row for each customer and split the record over multiple rows.
Orders:
ID Customer Shipdate Order ID
-----------------------------------------------------------------
1 Tom 2015-01-01 100
2 Bob 2014-03-20 200
3 John
4 Dan
5 2014-05-10 300
So there is another table I can join on to make sense of this which relates the three rows which are actually one order.
Joint.Orders:
ID Related ID
-----------------------------------------------------------------
5 3
5 4
I'm a little new to SQL and while I can join on the other table and filter to only get the data relating to Order ID 300, but what I'd really like is to concatenate the customers, but after searching for a while I can't see how to do this. What'd I'd really like to achieve is this as an output:
ID Customer Shipdate Order ID
----------------------------------------------------------------
1 Tom 2015-01-01 100
2 Bob 2014-03-20 200
5 John, Dan 2014-05-10 300
You should consider changing the schema first. The below query might help you get a feel of how it can be done with your current design.
Select * From Orders Where IsNull(Customer, '') <> ''
Union All
Select ID,
Customer = (Select Customer + ',' From Orders OI Where OI.ID (Select RelatedID from JointOrders JO Where JO.ID = O.ID)
,ShipDate, OrderID
From Orders O Where IsNull(O.Customer, '') = ''

SQL:How to get min Quantity?

I got this problem. When i tried to summarize the min quatity of nations's products and it did not work.
I have 2 tables below
PRODUCT:
ID|NAME |NaID|Qty
-------------------
01|Fruit|JP |50
02|MEAT |AUS |10
03|MANGA|JP |80
04|BOOK |AUS |8
NATION:
NaID |NAME
-------------------
AUS |Australia
JP |Japan
I want my result like this:
ID|NAME |Name|minQty
-------------------
01|Fruit|JP |50
04|BOOK |AUS |8
and i used:
select p.id,p.name, p.NaID,n.name,min(P.Qty)as minQty
from Product p,Nation n
where p.NaID=n.NaID
group by p.id,p.name, p.NaID,n.name,p.Qty
and i got this (T_T):
ID|NAME |NaID|minQty
-------------------
01|Fruit|JP |50
02|MEAT |AUS |10
03|MANGA|JP |80
04|BOOK |AUS |8
Please,Could soneone help me? I am thinking that i am bad at SQL now.
SQL Server 2005 supports window functions, so you can do something like this:
select id,
name,
NaID,
name,
qty
from (
select p.id,
p.name,
p.NaID,
n.name,
min(P.Qty) over (partition by n.naid) as min_qty,
p.qty
from Product p
join Nation n on p.NaID=n.NaID
) t
where qty = min_qty;
If there is more than one nation with the same minimum value, you will get each of them. If you don't want that, you need to use row_number()
select id,
name,
NaID,
name,
qty
from (
select p.id,
p.name,
p.NaID,
n.name,
row_number() over (partition by n.naid order by p.qty) as rn,
p.qty
from Product p
join Nation n on p.NaID = n.NaID
) t
where rn = 1;
As your example output with only includes the NaID but not the nation's name you don't really need the the join between product and nation.
(There is no DBMS product called "SQL 2005". SQL is just a (standard) for a query language. The DBMS product you mean is called Microsoft SQL Server 2005. Or just SQL Server 2005).
In Oracle, you can use several techniques. You can use subqueries and analytic functions, but the most efficient one is to use aggregate functions MIN and FIRST.
Your tables:
SQL> create table nation (naid,name)
2 as
3 select 'AUS', 'Australia' from dual union all
4 select 'JP', 'Japan' from dual
5 /
Table created.
SQL> create table product (id,name,naid,qty)
2 as
3 select '01', 'Fruit', 'JP', 50 from dual union all
4 select '02', 'MEAT', 'AUS', 10 from dual union all
5 select '03', 'MANGA', 'JP', 80 from dual union all
6 select '04', 'BOOK', 'AUS', 8 from dual
7 /
Table created.
The query:
SQL> select max(p.id) keep (dense_rank first order by p.qty) id
2 , max(p.name) keep (dense_rank first order by p.qty) name
3 , p.naid "NaID"
4 , n.name "Nation"
5 , min(p.qty) "minQty"
6 from product p
7 inner join nation n on (p.naid = n.naid)
8 group by p.naid
9 , n.name
10 /
ID NAME NaID Nation minQty
-- ----- ---- --------- ----------
01 Fruit JP Japan 50
04 BOOK AUS Australia 8
2 rows selected.
Since you're not using Oracle, a less efficient query, but probably working in all RDBMS:
SQL> select p.id
2 , p.name
3 , p.naid
4 , n.name
5 , p.qty
6 from product p
7 inner join nation n on (p.naid = n.naid)
8 where ( p.naid, p.qty )
9 in
10 ( select p2.naid
11 , min(p2.qty)
12 from product p2
13 group by p2.naid
14 )
15 /
ID NAME NAID NAME QTY
-- ----- ---- --------- ----------
01 Fruit JP Japan 50
04 BOOK AUS Australia 8
2 rows selected.
Note that if you have several rows with the same minimum quantity per nation, all those rows will be returned, instead of just one as in the previous "Oracle"-query.
with cte as (
select *,
row_number() over (partition by Nation order by qty) as [rn]
from product
)
select * from cte where [rn] = 1

How to select items according to their sums in SQL?

I've got the following table:
ID Name Sales
1 Kalle 1
2 Kalle -1
3 Simon 10
4 Simon 20
5 Anna 11
6 Anna 0
7 Tina 0
I want to write a SQL query that only returns the rows that
represents a salesperson with sum of sales > 0.
ID Name Sales
3 Simon 10
4 Simon 20
5 Anna 11
6 Anna 0
Is this possible?
You can easily get names of the people with the sum of sales that are greater than 0 by using the a HAVING clause:
select name
from yourtable
group by name
having sum(sales) > 0;
This query will return both Simon and Anna, then if you want to return all of the details for each of these names you can use the above in a WHERE clause to get the final result:
select id, name, sales
from yourtable
where name in (select name
from yourtable
group by name
having sum(sales) > 0);
See SQL Fiddle with Demo.
You can make it like this, I think the join will be more effective than the where name in() clause.
SELECT Sales.name, Sales.sales
FROM Sales
JOIN (SELECT name FROM Sales GROUP BY Sales.name HAVING SUM(sales) > 0) AS Sales2 ON Sales2.name = Sales.name
This will work on some databases, like oracle, mssql, db2
SELECT ID, Name, Sales
FROM
(
SELECT ID, Name, Sales, sum(sales) over (partition by name) sum1
FROM <table>
) a
WHERE sum1 > 0