I have a table with two columns, customer id and order.
Let's say I have in total order IDs 1,2,3,4
All the customer can have all the four orders, like below:
1234 1
1234 2
1234 3
1234 4
3245 3
3245 4
5436 2
5436 4
You can see above that 3245 customer doesn't have order id 1 or 2.
How could I print in the query output like:
3245 1
3245 2
5436 1
5436 3
EDIT: I don't have an order table, but I have a list of order's like we can hard code it in the query(1,2,3,4).
SELECT c.id, o.order
FROM (
SELECT 1 AS order
UNION ALL
SELECT 2 AS order
UNION ALL
SELECT 3 AS order
UNION ALL
SELECT 4 AS order
) o
CROSS JOIN
(
SELECT DISTINCT id
FROM customer_orders
) c
WHERE NOT EXISTS
(
SELECT NULL
FROM customer_orders ci
WHERE ci.id = c.id
AND ci.order = o.order
)
If you have customers table, it becomes more simple:
SELECT c.id, o.order
FROM (
SELECT 1 AS order
UNION ALL
SELECT 2 AS order
UNION ALL
SELECT 3 AS order
UNION ALL
SELECT 4 AS order
) o
CROSS JOIN
customers c
WHERE NOT EXISTS
(
SELECT NULL
FROM customer_orders ci
WHERE ci.id = c.id
AND ci.order = o.order
)
Okay, there are two issues here. The first problem is turning a list of numbers into a rowset. There are a number of different ways of doing this, depending on how you get the numbers into the query. In the following example I use a function which turns a comma-separated string into a nested table, which can be treated as a regular table with the TABLE() function. This is not strictly relevant to the question you pose. If you're interested in this bit of the implementation see my post in this other thread.
The second part of the problem is identifying the missing Orders for each Customer. The obvious approaches - such as using NOT IN with a sub-query - won't work, because the Orders for Customer 1234 match all the Order IDs. We need to do is fill in the missing orders for each Customer. This can be done by using a LEFT OUTER JOIN combined with the PARTITION BY clause. It is then a simple matter to filter out the hits by embedding the LOJ query in an outer SELECT, like this:
SQL> select customer_id
2 , missing_order_id
3 from (
4 select t42.customer_id
5 , t42.order_id
6 , nos.column_value as missing_order_id
7 from ( select * from table (str_to_number_tokens('1,2,3,4'))) nos
8 left outer join t42 partition by ( t42.customer_id )
9 on nos.column_value = t42.order_id
10 )
11 where order_id is null
12 /
CUSTOMER_ID MISSING_ORDER_ID
----------- ----------------
3245 1
3245 2
5436 1
5436 3
SQL>
aside from my comment, and your existing table, I would approach something like this...
select distinct
a.Customer,
b.OrderNumber
from
YourOrderTable a,
( select distinct OrderNumber from YourOrderTable ) b
where
b.OrderNumber NOT IN
( select OrderNumber from
YourOrderTable c
where a.Customer = c.Customer
and b.OrderNumber = c.OrderNumber )
By doing a select distinct as the second table in the FROM clause and no specific join to it, you will get a Cartesian join... ie: for each customer, it will join to every possible order number.
Then, in your WHERE clause, the NOT IN SQL test will only allow the "b." order numbers where none exist in the SQL-subselect (c.)
This could be a very costly query, especially if you have many unique orders..
Related
I've got two tables, one where customer ID is store and another that stores each date they had a purchase on. I am stuck on keeping all new customers that don't have a purchase date yet when querying for the max purchase date for all customers.
CustomerTable:
CustomerID
Full_Name
1
John Doe
2
Jane Doe
PurchaseDates:
CustomerID
Purchase_Date
1
11/21/2021
1
4/19/2003
I have set up a view in SQL that combines the two and queries for the MAX purchase date for each customer. The problem is that since I am using MAX, customers that have not purchased anything yet do not show up as they either do not have an entry in PurchaseDates table or their purchase_date field is blank.
My SQL View Code:
SELECT ct.CustomerID,
ct.Full_Name,
pd.Purchase_Date,
FROM CustomerTable AS ct
LEFT OUTER JOIN PurchaseDates AS pd
ON ct.CustomerID = pd.CustomerID
WHERE EXISTS (SELECT 1
FROM PurchaseDates AS pd_latest
WHERE ( CustomerID= pd.CustomerID)
GROUP BY CustomerID
HAVING ( Max(Purchase_Date) = pd.Purchase_Date))
The result in my example above yields only customerID 1 with the purchase date of 11/21/2021, but I'd like to also display CustomerID 2 with a null date for their purchase_date. Not really sure how to proceed apart from seeing that some have opted to replace all nulls with arbitrary days.
The end result should be
CustomerID
Full_Name
Purchase_Date
1
John Doe
11/21/2021
2
Jane Doe
Appreciate the help
You only need a single value from the PurchaseDates table so a simple correlated subquery is all you require:
select ct.CustomerID, ct.Full_Name,
(
select Max(pd.Purchase_Date)
from PurchaseDates pd
where pd.CustomerId = ct.CustomerId
) as Purchase_Date
from CustomerTable ct;
Should more than a single column be required then you could apply the appropriate row:
select ct.CustomerID, ct.Full_Name, pd.*
from CustomerTable ct
outer apply (
select top(1) *
from PurchaseDates pd
where pd.CustomerId = ct.CustomerId
order by pd.Purchase_date desc
)pd;
Another version of the correlated subquery :
select *
from (
(select Full_name,
your_date,
(select max(your_date) from PurchaseDates c where c.id=A.id ) as max_date
from CustomerTable A
LEFT JOIN PurchaseDates B ON A.ID =B.ID)) x
where (x.max_date = your_date) or your_date is null or max_date is null
I have 2 tables very similar, they both partially have same column's name (and datatype), so instead of having to select tables 1 by 1, I wanted to make it so the first table's column become same like second's table column (so like if they have 4 columns with same name, instead of having 8 column after selecting, it shows only 3)
JOIN items i ON i.characterId=c.characterId
WHERE i.itemId=18011
SELECT c.accountId,c.characterId,c.name,b.itemId,b.maxUpgrade,b.amount FROM characters c
JOIN bankItems b ON b.accountId=c.accountId
WHERE b.itemId=18011
here is an example of request I do to select a same info from both tables, I need to do 2 different request and I wish I could fusion them
table 1 (characters):
characterId accountId name
table 2 (items):
characterId itemId maxUpgrade amount
table 3 (bankItems) :
accountId itemId maxUpgrade amount
And in result :
accountId characterId name itemId maxUpgrade amount
but all in 1 request, so no need to type the WHERE c.name= twice
You could do a union of items and bankItems tables within a CTE and then join the characters table on the CTE for example with either the accountId or characterId:
;WITH CTE AS (
SELECT itemId, NULL AS characterId, accountId, maxUpgrade, amount
FROM bankItems
UNION
SELECT itemId, characterId, NULL AS accountId, maxUpgrade, amount
FROM items
)
SELECT
c.accountId,
c.characterId,
c.name,
b.itemId,
b.maxUpgrade,
b.amount
FROM characters c
JOIN CTE b ON
b.accountId = c.accountId
OR b.characterId = c.characterId
WHERE b.itemId = 18011;
Considering the table structure this solution with optional fields should work.
I have 2 table like that:
Table1:
ID | COMPANY_NAME | Rank | FIRST_REGIST_DATE
1 A 1 2017-09-01
2 B 2 2017-09-05
Table 2:
ID | COMPANY_NAME | RANK | FIRST_REGIST_DATE
1 A 3 2017-09-03
2 C 4 2017-09-04
I need to SELECT company with the data FIRST_REGIST_DATE and RANK
In case of company have 2 first regist date, we choose the earlier date and RANK greater
(EX: company A above will have first date: 2017-09-01)
The Expect result will like that:
Company A - Rank 3 - Date:2017-09-01
Please have me to SELECT in that case
This technically answers the question but avoids the elephant in the room (which ID takes preference?). As both tables have ID's that may overlap ({B,C} have an ID of 2) rules need to be defined as to which ID takes preference what will the other table ID's be renamed to.
Select COMPANY_NAME
,MIN(FIRST_REGIST_DATE) as REGIST_DATE
from (
SELECT *
FROM #table1
UNION ALL
SELECT *
FROM #table2
) t3
Group by COMPANY_NAME
OP dont change your question (by adding RANK) after the question has been answered.
For your changes: kindly contributed by #toha
Select COMPANY_NAME
,MIN(FIRST_REGIST_DATE) as REGIST_DATE
,MAX(RANK ) as RANK
from ( SELECT *
FROM #table1
UNION ALL
SELECT *
FROM #table2 ) t3
Group by COMPANY_NAME
If I understand the question correctly you have two tables with data containing company details and first registration date and you want to show the row with the earliest first registration date. The following query will help you.
SELECT company_name, MIN(fisrt_regist_date)
(
SELECT company_name, fisrt_regist_date
FROM table1
UNION ALL
SELECT company_name, fisrt_regist_date
FROM table2
) tab1
FROM tab1
GROUP BY company_name
The above query will combine the results of the first table and the second table and then show you the details of the company along with the oldest registration date.
I have a table with over 100k records. Here my issue, I have a bunch of columns
CompanyID CompanyName CompanyServiceID ServiceTypeID Active
----------------------------------------------------------------
1 Xerox 17 33 Yes
2 Microsoft 19 39 Yes
3 Oracle 22 54 Yes
2 Microsoft 19 36 Yes
So here's how my table looks, it has about 30 other columns but they are irrelevant for this question.
Here's my quandary..I'm trying to select all records where CompanyID and CompanyServiceID are the same, so basically as you can see in the table above, I have Microsoft that appears twice in the table, and has the same CompanyID and CompanyServiceID, but different ServiceTypeID.
I need to be able to search all records where there are duplicates. The person maintaining this data was very messy and did not update some of the columns properly so I have to go through all the records and find where there are records that have the same CompanyID and CompanyServiceID.
Is there a generic query that would be able to do that?
None of these columns are my primary key, I have a column with record number that increments by 1.
You can try something like this:
SELECT CompanyName, COUNT(CompanyServiceID)
FROM //table name here
GROUP BY CompanyName
HAVING ( COUNT(CompanyServiceID) > 1 )
This will return a grouped list of all companies with multiple entries. You can modify what columns you want in the SELECT statement if you need other info from the record as well.
Here's one option using row_number to create the groupings of duplicated data:
select *
from (
select *,
row_number () over (partition by companyId, companyserviceid
order by servicetypeid) rn
from yourtable
) t
where rn > 1
Another option GROUP BY, HAVING and INNER JOIN
SELECT
*
FROM
Tbl A INNER JOIN
(
SELECT
CompanyID,
CompanyServiceID
FROM
Tbl
GROUP BY
CompanyID,
CompanyServiceID
HAVING COUNT(1) > 1
) B ON A.CompanyID = B.CompanyID AND
A.CompanyServiceID = B.CompanyServiceID
Using Join..
Select *
from
Yourtable t1
join
(
select companyid,companyserviceid,count(*)
from
Yourtable
having count(*)>1)b
on b.companyid=t1.companyid
and b.companyserviceid=t1.companyserviceid
I have two tables, Master and Child. I need the sum of column Cash in the Master table and also I am referring the Child table to match certain conditions.
Master Table:
ID CASH BillDate
1 100 22-02-2014
2 200 22-02-2014
Child Table:
ChildID MasterID
1 1
2 1
3 2
My Query:
select CONVERT(varchar,BillDate,103) as BillDate,SUM(cash)as ByCash
from childdetails CD
inner join MasterDetails MD on MD.ID=CD.MasterID
where CONVERT(varchar,BillDate,103)='22/02/2014'
group by BillDate
My Incorrect Output:
BillDate ByCash
22/02/2014 400
The correct output should be 300 in ByCash, but I'm not sure why its being calculated to be 400.
The issue appears to be that your Child Table counts the MasterID twice. Try selecting the Child table as a CTE with a row_number, partitioned by MasterID to filter out duplicates:
select
CONVERT(varchar,BillDate,103) as BillDate,
SUM(cash)as ByCash
from (
select *,
row_number() over(partition by MasterID order by ChildID) dedupe
from childdetails
) CD inner join MasterDetails MD on MD.ID=CD.MasterID
where
CONVERT(varchar,BillDate,103)='22/02/2014'
and CD.dedupe = 1
group by BillDate
Maybe this is what you're looking for:
SELECT BillDate, SUM(Cash) FROM MasterDetails GROUP BY BillDate
If it isn't, please clarify exactly what your expected output is.