SQL query to perform a lookup with transpose - sql

I would like to achieve the following and to be honest, I don't even know where to start. We have two tables, Customers and Orders. I need to create a third table, which will have combined data, and displayed in a horizontal way.
Those are the current tables:
CUSTOMERS:
Id Email Language
Customer1 1 cust1#email.com en
Customer2 2 cust2#email.com sp
Customer3 3 cust3#email.com ru
ORDERS:
Id CustomerId Total
a 1 200
b 1 300
c 2 400
d 3 500
e 3 500
f 3 500
g 3 500
And the desired outcome:
CustomerID Email Language Order1 Order2 Order3 Order4 Order5 Order6
1 a b - - - -
2 c - - - - -
3 d e f g - -
Each customer can have up to 6 active orders, but the logic can also be that for each customer only the 6 first orders will be listed.
Any suggestions on how to achieve this result? Your help will be greatly appreciated.

SQL tables represent unordered tables. There is no ordering unless a column specifies the ordering. Let me assume that id plays that role.
Then, you can do this with conditional aggregation:
select c.id, c.email, c.language,
max(case when seqnum = 1 then o.id end) as order_1,
max(case when seqnum = 2 then o.id end) as order_2,
max(case when seqnum = 3 then o.id end) as order_3,
max(case when seqnum = 4 then o.id end) as order_4,
max(case when seqnum = 5 then o.id end) as order_5,
max(case when seqnum = 6 then o.id end) as order_6
from customers c left join
(select o.*,
row_number() over (partition by customerid order by id) as seqnum
from orders o
) o
on c.customerid = o.customerid
group by c.id, c.email, c.language;

Related

SQL get record when finding in 2 records

Hi I tried to build sql query to find id when is in 2 records (can be more). Let me explained by example
I have 2 tables
C
id
type_id
1
499
1
599
D
type_id
type_name
499
AN
599
DE
And I want to get id which has AN and DE
SELECT *
FROM C
INNER JOIN D
ON D.type_id = C.type_id
WHERE
EXISTS (SELECT 1 FROM D D1 WHERE D1.type_id = C.type_id AND D1.type_name = 'AN') AND
EXISTS (SELECT 1 FROM D D2 WHERE D2.type_id = C.type_id AND D2.type_name = 'DE');
But did not work .Than you for help
If you want all the data from the join then you can use analytic functions:
SELECT id,
type_id,
type_name
FROM (
SELECT c.id,
c.type_id,
d.type_name,
COUNT(CASE d.type_name WHEN 'AN' THEN 1 END) OVER (PARTITION BY c.id)
AS num_an,
COUNT(CASE d.type_name WHEN 'DE' THEN 1 END) OVER (PARTITION BY c.id)
AS num_de
FROM C
INNER JOIN D
ON D.type_id = C.type_id
WHERE d.type_name IN ('AN', 'DE')
)
WHERE num_an > 0
AND num_de > 0;
Which outputs:
ID
TYPE_ID
TYPE_NAME
1
599
DE
1
499
AN
If you just want the id then you can aggregate and use a HAVING clause:
SELECT c.id
FROM C
INNER JOIN D
ON D.type_id = C.type_id
WHERE d.type_name IN ('AN', 'DE')
GROUP BY c.id
HAVING COUNT(CASE d.type_name WHEN 'AN' THEN 1 END) > 0
AND COUNT(CASE d.type_name WHEN 'DE' THEN 1 END) > 0
Which outputs:
ID
1
fiddle
Get the distinct counts of type_name for each ID ensure count = two and limit type_name to 'AN' or 'DE'
SELECT C.ID
FROM C
INNER JOIN D
on C.type_id=D.type_id -- standard join on Type_ID
WHERE D.Type_name in ('AN','DE') -- limit records to only AN/DE since we need both.
GROUP BY C.ID -- group so we get just 1 ID
HAVING Count(Distinct Type_name) = 2 -- ensure distinct count is 2 for each C.ID.
We join the two tables
We limit to ID having either an 'AN' or DE type name
We group by ID's
We count the distinct types for each ID and if it's 2, we know we have an AN and DE type for that ID.
Count distinct is used since I'm unsure if a type_name could be duplicated for a C.ID. It looks possible given table structure. but unsure without known Pk/FK relations. distinct "Might" be able to be removed if we KNOW it's not possible.

To select data from multiple records in SQL Server having a common ID

I need to select/concat data from 2 tables in SQL Server I'm using Left Join, but the data is returned as multiple records.
Below are the sample tables
Table1
Id Name Age
1 Sk 20
2 Rb 30
Table2
ID Bike Price Table1Id
1 RX 200 1
2 CD 250 1
3 FZ 300 1
4 R1 400 2
The desired output is
ID Name Age Bike1 Price1 Bike2 Price2 Bike3 Price3
1 Sk 20 RX 200 CD 250 FZ 300
2 Rb 30 R1 400 NULL NULL NULL NULL
A sample format of the query I'm using
SELECT A.ID, A.Name, B.Bike, B.Price FROM Table1 A LEFT JOIN Table2 B ON
A.id = B.Table1Id order by A.id
The output I'm getting from the above query is
ID Name Age Bike Price
1 Sk 20 RX 200
1 Sk 20 CD 250
1 Sk 20 FZ 300
2 Rb 30 R1 400
I need the data as one record for a particular ID and not multiple records (As seen in the desired output). Tired using offset, but offset will return only limited result not the entire records.
Any suggestions on how this can be achieved?
If you know the maximum number of bikes per person, you can use conditional aggregation:
SELECT ID, Name,
MAX(CASE WHEN seqnm = 1 THEN Bike END) as bike_1,
MAX(CASE WHEN seqnm = 1 THEN Price END) as price_1,
MAX(CASE WHEN seqnm = 2 THEN Bike END) as bike_2,
MAX(CASE WHEN seqnm = 2 THEN Price END) as price_2,
MAX(CASE WHEN seqnm = 3 THEN Bike END) as bike_3,
MAX(CASE WHEN seqnm = 3 THEN Price END) as price_3
FROM (SELECT A.ID, A.Name, B.Bike, B.Price,
ROW_NUMBER() OVER (PARTITION BY A.id ORDER BY B.Price) as seqnum
FROM Table1 A LEFT JOIN
Table2 B
ON A.id = B.Table1Id
) ab
GROUP BY ID, Name,
ORDER BY id

SQL getting ordered row number per customer

I have a database for a pet shop, and I'm trying to get the most bought pets per customer. So if a customer bought 6 mice, 3 birds, 2 cats, 3 dogs, I'm trying to get the following:
Customer ID Animal Count
----------- ------ -----
1 mouse 6
1 bird 3
1 dog 3
However, in order to do that, I need to group by animal and customer ID, and create a row number for each record by count.
I have 3 tables:
Orders
Order contents (i.e animals in the order)
Animal details (i.e type of animal, cost, etc.)
Here is my query so far:
SELECT customer_id, animal, count(*) as cnt, row_number() over (order by count(*)) as seqnum
FROM [Order_Contents] cc
INNER JOIN [Animals] p on cc.animal_id = p.animal_id
INNER JOIN [Orders] o ON cc.order_id = o.order_id
WHERE customer_id = 1
GROUP BY animal, customer_id
ORDER BY customer_id, seqnum
Here is what I expect:
Customer ID Animal Count seqnum
----------- ------ ----- ------
1 mouse 6 1
1 bird 3 2
1 dog 3 3
1 cat 2 4
However, the sequential number isn't per customer, it's just sequential for the whole result set:
Customer ID Animal Count seqnum
----------- ------ ----- ------
1 mouse 6 98
1 bird 3 33
1 dog 3 36
1 cat 2 15
What am I doing wrong here? I need seqnum to be able to do "top 3" per customer later.
The challenge is getting the highest total for a customer with the details.
This returns the information:
SELECT o.customer_id, a.animal, COUNT(*) as cnt,
SUM(COUNT(*)) OVER (PARTITION BY o.customer_id) as customer_cnt
FROM Order_Contents cc INNER JOIN
Animals a
ON cc.animal_id = a.animal_id INNER JOIN
Orders o ON cc.order_id = o.order_id
WHERE customer_id = 1
GROUP BY animal, customer_id
ORDER BY customer_cnt DESC;
To get the details for the customer with the highest count, you can use the TOP WITH TIES trick:
SELECT TOP (1) WITH TIES ca.*
FROM (SELECT o.customer_id, a.animal, COUNT(*) as cnt,
SUM(COUNT(*)) OVER (PARTITION BY o.customer_id) as customer_cnt
FROM Order_Contents cc INNER JOIN
Animals a
ON cc.animal_id = a.animal_id INNER JOIN
Orders o ON cc.order_id = o.order_id
WHERE customer_id = 1
GROUP BY a.animal, o.customer_id
) ca
ORDER BY DENSE_RANK() OVER (ORDER BY customer_cnt DESC);
You need to add the partition so seq reset
row_number() over (partition by customer_id order by count(*))

SQL condition sum from two joined tables

I have two tables as below:
Invoice
InvId | Amount | Name
-----------------------
1 | 50 | John
2 | 30 | Mike
3 | 20 | John
Detail
MetalType| Weight | InvId
-------------------------
Gold | 2 | 2
Silver | 4 | 3
Silver | 3 | 3
Gold | 5 | 1
I would like to have the following output, but my query will only provide the total for silver and gold for John. How can I build a query that will also include the total invoice amount for John.
Total Invoice Amount For John = 70
Total Silver Weight = 7
total Gold Weith = 5
SELECT
SUM(IFF(D.MetalType=”Gold”, D.Weight, 0)) AS TotGold,
SUM((IFF(D.MetalType=”Silver”, D.Weight, 0)) AS TotSilver
FROM Invoice I INNER JOIN Detail D ON I.InvId = D.InvId WHERE I.Name = “John”
Try this:
For Sql-Server:
SELECT
SUM(TotalAmount) AS TotalAmount,
SUM(TotGold) AS TotGold,
SUM(TotSilver) AS TotSilver
FROM(
SELECT
SUM (I.Amount) OVER (Partition by D.Invid) AS TotalAmount,
SUM(CASE WHEN D.MetalType='Gold' THEN D.Weight ELSE 0 END) AS TotGold,
SUM(CASE WHEN D.MetalType='Silver' THEN D.Weight ELSE 0 END) AS TotSilver
FROM Invoice I INNER JOIN Detail D ON I.InvId = D.InvId
WHERE I.Name = 'John'
GROUP BY D.InvId, I.Amount) n
Here is an SQL Fiddle - now it kills the duplicate detail and counts it only once.
EDITED for Access:
SELECT
n.Name,
MAX(TotalAmount),
SUM(TotGold) AS TotGold,
SUM(TotSilver) AS TotSilver
FROM(
SELECT
I.Name,
SUM(CASE WHEN D.MetalType='Gold' THEN D.Weight ELSE 0 END) AS TotGold,
SUM(CASE WHEN D.MetalType='Silver' THEN D.Weight ELSE 0 END) AS TotSilver
FROM Invoice I
INNER JOIN Detail D ON I.InvId = D.InvId
GROUP BY I.Name, D.InvId, I.Amount) n
INNER JOIN (
SELECT
I.Name, SUM (I.Amount) AS TotalAmount
FROM Invoice I
GROUP BY I.Name) m ON m.Name = n.Name
GROUP BY n.Name
Try with this:
With tbl3 (Amt,Gold,Silver)
as
(
SELECT
SUM (I.Amount) OVER (Partition by D.Invid) AS TotalAmount,
SUM(CASE WHEN D.MetalType='Gold' THEN D.Weight ELSE 0 END) AS TotGold,
SUM(CASE WHEN D.MetalType='Silver' THEN D.Weight ELSE 0 END) AS TotSilver
FROM Invoice I Right JOIN Detail D ON I.InvId = D.InvId
WHERE I.Name = 'John' Group by D.InvId, I.Amount
)
Select SUM(Amt) as Total_Invoice_Amount_For_John,
SUM(Gold) as Total_Silver_Weight,
SUM(Silver) as Total_Gold_Width from tbl3
SQL Fiddle
I havent tried out the other queries already posted and they might already be suitable for what you want but here's my take on it :-
SELECT X.NAME, X.METALTYPE, X.WEIGHT, Y.TOTAL
FROM
(SELECT NAME, METALTYPE, SUM(Weight) AS WEIGHT
FROM INVOICE i
INNER JOIN DETAIL d ON i.InvId = d.InvId
GROUP BY NAME, METALTYPE) X
INNER JOIN
(SELECT SUM(AMOUNT) AS Total, NAME
FROM INVOICE
GROUP BY NAME)Y
ON X.NAME = Y.NAME
ORDER BY NAME, TOTAL, METALTYPE
select name, sum(Amount) as 'total invoice',sum(Gold) as 'Gold',sum(Silver) as Silver from(
select aa.Name,aa.Amount,
sum(case when bb.MetalType='Gold' then bb.Weight else 0 end) as 'Gold',
sum(case when bb.MetalType='Silver' then bb.Weight else 0 end) as 'Silver'
from a aa left outer join b bb on aa.InvID=bb.InvID group by aa.InvID) as c group by c.name

Assistance needed with join query

I have two very simple tables in Oracle 9G:
Customer
userid | firstName | lastName | email
-------+-----------+----------+---------------------
1 user1 User1 user1#mail.in
2 user2 User2 user2#mail.in
Order
orderiD | userId | OrderType | Order_Date | Amount
--------+--------+-----------+------------+-------
1 1 0 12/12/2009 1
2 1 1 13/12/2009 2
3 1 1 14/12/2009 3
4 2 0 12/12/2009 4
5 2 1 16/12/2009 2
6 1 0 14/12/2009 5
7 2 1 17/12/2009 4
8 2 0 10/12/2010 2
I want to select all users which have orders of type 0
select *
from Customer c
inner join Order o on c.userid = o.userid
where o.orderType = '0'
The result is:
orderiD | userId | OrderType | Order_Date | Amount
--------+--------+-----------+------------+--------
1 1 0 12/12/2009 1
4 2 0 12/12/2009 4
6 1 0 14/12/2009 5
8 2 0 10/12/2010 2
Now I need to modify this query to bring only last purchase date for each user id and get the result like this:
orderiD | userId | OrderType | Order_Date | Amount
--------+--------+-----------+------------+--------
6 1 0 14/12/2009 5
8 2 0 10/12/2010 2
How should I modify my query to get this result?
SELECT *
FROM order o
WHERE o.orderType ='0'
AND o.order_date =
( SELECT MAX(o2.order_date)
FROM order o2
WHERE o2.userid = o.userid
AND o2.orderType = '0'
)
or
SELECT o.*
FROM order o
JOIN
( SELECT userid
, MAX(order_date) AS lastPurchaseDate
FROM order
WHERE o.orderType ='0'
GROUP BY userid
) AS grp
ON grp.userid = o.userid
AND grp.lastPurchaseDate = o.order_date
Try this:
select *
from customer c
, order o
where c.userid = o.userid
and o.orderType ='0'
and o.order_date = (
select max(o2.order_date)
from order o2
where o2.userid = o.userid
)
No subquery required, just aggregate functions.
select c.userId, o.OrderType,
max(o.Order_Date),
max(o.orderiD) keep (dense_rank last order by order_date),
max(o.Amount) keep (dense_rank last order by order_date)
from Customer c inner join Order o
on c.userid = o.userid where o.orderType ='0'
group by c.userId, o.OrderType
SELECT *
FROM Order o
INNER JOIN
(SELECT MAX(o.id) AS maxid
FROM Customer c
INNER JOIN Order o ON c.userid = o.userid
WHERE o.orderType ='0'
GROUP BY c.userid) x ON x.maxid = o.id