SQL Query Using AND OR - sql

I have two tables: Customers and Orders.
I need to know which Customers have ordered "Bread" AND (Milk or Ice Cream) in a given month.
How should I write the SQL query?
Thanks.

As others have said in the comments, you should really try to improve your question so that people are more inclined to answer and help you.
Setting up some test data (making assumptions about your schema):
CREATE TABLE Customers (
CustomerID INT,
CustomerName VARCHAR(50)
)
CREATE TABLE Orders (
OrderId INT,
OrderDate DATETIME,
CustomerID INT,
Product VARCHAR(50)
)
INSERT INTO Customers
SELECT 1, 'John Smith'
UNION
SELECT 2, 'Jane Doe'
UNION
SELECT 3, 'Rachel Booth'
UNION
SELECT 4, 'Steve Barnes'
UNION
SELECT 5, 'Nancy Green'
UNION
SELECT 6, 'Phil Jones'
INSERT INTO Orders
SELECT 1, '2016-09-01', 1, 'Bread'
UNION
SELECT 2, '2016-09-10', 3, 'Milk'
UNION
SELECT 3, '2016-09-19', 1, 'Milk'
UNION
SELECT 4, '2016-10-07', 4, 'Bread'
UNION
SELECT 5, '2016-10-13', 2, 'Milk'
UNION
SELECT 6, '2016-10-15', 4, 'Milk'
UNION
SELECT 7, '2016-10-19', 6, 'Milk'
UNION
SELECT 8, '2016-10-21', 6, 'Ice Cream'
UNION
SELECT 9, '2016-10-27', 1, 'Milk'
UNION
SELECT 10, '2016-10-28', 4, 'Milk'
UNION
SELECT 11, '2016-11-06', 5, 'Ice Cream'
UNION
SELECT 12, '2016-11-09', 5, 'Bread'
And setting the desired month to check:
DECLARE #Month DATETIME
SET #Month = '2016-09-01'
I would get a list of Customers fitting your logic like this:
SELECT DISTINCT c.CustomerName
FROM Customers c
JOIN Orders ob ON ob.CustomerID = c.CustomerID
JOIN Orders omi ON omi.CustomerID = c.CustomerID
WHERE ob.Product = 'Bread'
AND omi.Product IN ('Milk','Ice Cream')
AND DATEADD(month, DATEDIFF(month, 0, ob.OrderDate), 0) = DATEADD(month, DATEDIFF(month, 0, omi.OrderDate), 0)
AND DATEADD(month, DATEDIFF(month, 0, ob.OrderDate), 0) = #Month

Related

Getting "column ambiguously defined" without any join in code

I get "column ambiguously defined" error while trying to execute following script:
INSERT INTO experience (id_, company, start_, end_, usr, position_)
WITH exp_ AS (
SELECT 0, 0, DATE'2013-04-30', NULL, 11, 'CEO' FROM DUAL UNION ALL
SELECT 1, 0, DATE'2011-12-11', DATE'2013-04-30', 9, 'Administrator' FROM DUAL UNION ALL
SELECT 2, 0, DATE'2013-04-30', DATE'2015-10-27', 6, 'Manager' FROM DUAL UNION ALL
SELECT 3, 1, DATE'2014-11-04', NULL, 10, 'CEO' FROM DUAL UNION ALL
SELECT 4, 1, DATE'2017-06-13', NULL, 13, 'CTO' FROM DUAL UNION ALL
SELECT 5, 1, DATE'2010-04-22', DATE'2018-10-22', 1, 'Manager' FROM DUAL UNION ALL
SELECT 6, 1, DATE'2019-08-26', NULL, 2, 'Trainer' FROM DUAL UNION ALL
SELECT 7, 1, DATE'2006-02-27', DATE'2016-11-30', 6, 'Video Creator' FROM DUAL UNION ALL
SELECT 8, 2, DATE'2012-04-30', DATE'2013-11-24', 5, 'CEO' FROM DUAL UNION ALL
SELECT 9, 2, DATE'2013-01-10', NULL, 0, 'CEO' FROM DUAL UNION ALL
SELECT 10, 2, DATE'2010-11-01', DATE'2017-01-01', 7, 'Video Creator' FROM DUAL UNION ALL
SELECT 11, 2, DATE'2010-10-03', DATE'2018-03-01', 8, 'Commentator' FROM DUAL
)
SELECT * FROM exp_;
I don't use any JOIN here, and the error description says, that it's caused by trying to join two tables with overlapping column's names.
So why I got it here and how to fix it?
This syntax would work only if you drop the CTE and directly insert the rows with UNION ALL:
INSERT INTO experience (id_, company, start_, end_, usr, position_)
SELECT 0, 0, DATE'2013-04-30', NULL, 11, 'CEO' FROM DUAL UNION ALL
SELECT 1, 0, DATE'2011-12-11', DATE'2013-04-30', 9, 'Administrator' FROM DUAL UNION ALL
SELECT 2, 0, DATE'2013-04-30', DATE'2015-10-27', 6, 'Manager' FROM DUAL UNION ALL
SELECT 3, 1, DATE'2014-11-04', NULL, 10, 'CEO' FROM DUAL UNION ALL
SELECT 4, 1, DATE'2017-06-13', NULL, 13, 'CTO' FROM DUAL UNION ALL
SELECT 5, 1, DATE'2010-04-22', DATE'2018-10-22', 1, 'Manager' FROM DUAL UNION ALL
SELECT 6, 1, DATE'2019-08-26', NULL, 2, 'Trainer' FROM DUAL UNION ALL
SELECT 7, 1, DATE'2006-02-27', DATE'2016-11-30', 6, 'Video Creator' FROM DUAL UNION ALL
SELECT 8, 2, DATE'2012-04-30', DATE'2013-11-24', 5, 'CEO' FROM DUAL UNION ALL
SELECT 9, 2, DATE'2013-01-10', NULL, 0, 'CEO' FROM DUAL UNION ALL
SELECT 10, 2, DATE'2010-11-01', DATE'2017-01-01', 7, 'Video Creator' FROM DUAL UNION ALL
SELECT 11, 2, DATE'2010-10-03', DATE'2018-03-01', 8, 'Commentator' FROM DUAL
See the demo.
You columns do not have names in the CTE. Define it with names:
WITH exp_(id_, company, start_, end_, usr, position_) AS (
Here is a db<>fiddle.
You need to give aliases to your columns. It is enough to do it in the first unioned subquery:
INSERT INTO experience (id_, company, start_, end_, usr, position_)
WITH exp_ AS (
SELECT
0 id_,
0 company,
DATE'2013-04-30' start_,
NULL end_,
11 usr,
'CEO' position
FROM DUAL UNION ALL
SELECT 1, 0, DATE'2011-12-11', DATE'2013-04-30', 9, 'Administrator' FROM DUAL UNION ALL
SELECT 2, 0, DATE'2013-04-30', DATE'2015-10-27', 6, 'Manager' FROM DUAL UNION ALL
SELECT 3, 1, DATE'2014-11-04', NULL, 10, 'CEO' FROM DUAL UNION ALL
SELECT 4, 1, DATE'2017-06-13', NULL, 13, 'CTO' FROM DUAL UNION ALL
SELECT 5, 1, DATE'2010-04-22', DATE'2018-10-22', 1, 'Manager' FROM DUAL UNION ALL
SELECT 6, 1, DATE'2019-08-26', NULL, 2, 'Trainer' FROM DUAL UNION ALL
SELECT 7, 1, DATE'2006-02-27', DATE'2016-11-30', 6, 'Video Creator' FROM DUAL UNION ALL
SELECT 8, 2, DATE'2012-04-30', DATE'2013-11-24', 5, 'CEO' FROM DUAL UNION ALL
SELECT 9, 2, DATE'2013-01-10', NULL, 0, 'CEO' FROM DUAL UNION ALL
SELECT 10, 2, DATE'2010-11-01', DATE'2017-01-01', 7, 'Video Creator' FROM DUAL UNION ALL
SELECT 11, 2, DATE'2010-10-03', DATE'2018-03-01', 8, 'Commentator' FROM DUAL
)
SELECT * FROM exp_;
For your use case, you could also consider using the insert all syntax, which avoids the multiple union alls:
insert all
into experience (id_, company, start_, end_, usr, position_)
values(0, 0, date '2013-04-30', null, 11, 'CEO')
into experience (id_, company, start_, end_, usr, position_)
values(1, 0, date '2011-12-11', date '2013-05-30', 9, 'Administrator')
...
select 1 from dual

Lead and partition function

This is my input table
create table #table1 (id int, FN varchar(20), startdate varchar(20), id1 varchar)
insert #table1
select 1, 'Joe', '2019-01-01', 'A'
union select 1, 'Joe', '2019-01-01', 'B'
union select 1, 'Joe', '2019-01-05', 'C'
union select 1, 'Joe', '2019-01-05', 'D'
union select 1, 'Joe', '2019-01-06', 'E'
union select 2, 'john', '2019-01-05', 'F'
union select 2, 'john', '2019-01-06', 'G'
union select 2, 'john', '2019-01-06', 'H'
union select 2, 'john', '2019-01-07', 'I'
I tried the following code
select *
, dense_rank() OVER (partition by id, fn order by startdate)
, lead(startdate,1) OVER (partition by id, fn order by startdate)
from #table1
order by id
But I require the following output:
I know that there might be a better approach but a least this is a working solution:
select *,
(select MIN(startdate)
from #table1 t1
where t1.id = #table1.id and
t1.fn = #table1.fn and
t1.startdate > #table1.startdate) enddate
from #table1
Result

SQL Server 2016 GROUP BY + HAVING only get different records by JOIN

We have two tables, one for users and one for records. Records have a reference number and link to a user by a FK. We want to get reference numbers from the records table where the same reference number was used for different users. Two users are the same user if they have the same last name and same date of birth. Two users are different users if either last name or DOB are different.
Sample data:
DECLARE #user TABLE (id INT, fname VARCHAR, lname VARCHAR, dob VARCHAR(3))
DECLARE #record TABLE (id INT, refno VARCHAR, fk INT)
--first two are the same people because same firstname + lastname + date of birth
INSERT INTO #user SELECT 1, 'a', 'a', 'jan'
INSERT INTO #user SELECT 2, 'a', 'a', 'jan'
--next two are the same people because same firstname + lastname + date of birth
INSERT INTO #user SELECT 3, 'b', 'b', 'feb'
INSERT INTO #user SELECT 4, 'b', 'b', 'feb'
--next two are different people because same firstname + lastname but different date of birth
INSERT INTO #user SELECT 5, 'c', 'c', 'mar'
INSERT INTO #user SELECT 6, 'c', 'c', 'apr'
--next two are different people because same firstname + date of birth but but different surname
INSERT INTO #user SELECT 7, 'd', 'e', 'may'
INSERT INTO #user SELECT 8, 'd', 'f', 'may'
--next two are different people because same firstname + date of birth but but different surname
INSERT INTO #user SELECT 9, 'd', 'e', 'may'
INSERT INTO #user SELECT 10, 'd', 'f', 'may'
--with simplified ref nos
INSERT INTO #record SELECT 10, '1', 1
INSERT INTO #record SELECT 11, '1', 2
INSERT INTO #record SELECT 12, '2', 3
INSERT INTO #record SELECT 13, '2', 4
INSERT INTO #record SELECT 14, '3', 5
INSERT INTO #record SELECT 15, '3', 6
INSERT INTO #record SELECT 16, '3', 7
INSERT INTO #record SELECT 17, '3', 8
INSERT INTO #record SELECT 18, 'a', 9
INSERT INTO #record SELECT 19, 'a', 10
What I've tried:
SELECT r.refno
, count(1) cnt
FROM #record r
INNER JOIN #user u ON u.id = r.fk
GROUP BY u.lname
, r.refno
, u.dob
HAVING COUNT(1) > 1
Which returns:
refno cnt
1 2
2 2
but the expected output is:
refno cnt
3 4
a 2
Thanks.
Just fix your SQL Query with below :
SELECT A.refno,
SUM(cnt) cnt
FROM
(
SELECT r.refno,
COUNT(*) cnt
FROM #record r
INNER JOIN #user u ON u.id = r.fk
GROUP BY u.lname,
r.refno,
u.dob
) A
WHERE A.cnt = 1
GROUP BY A.refno;
Result :
refno cnt
3 4
a 2
You could try this:
SELECT refno
, COUNT(1) AS cnt
FROM (
SELECT DISTINCT u.lname
, r.refno
, u.dob
FROM #record r
INNER JOIN #user u ON u.id = r.fk
) AS t
GROUP BY refno
HAVING COUNT(1) > 1;

Query multiple records of a group that has max date....sorta

Example code:
DECLARE #TABLE TABLE (ID int, Name varchar(50), Date Datetime2)
INSERT INTO #TABLE (ID, Name, Date)
SELECT 1, 'abc', GETDATE()
UNION ALL
SELECT 1, 'def', GETDATE()
UNION ALL
SELECT 1, 'hij', GETDATE()
UNION ALL
SELECT 1, 'abc', GETDATE()-2
UNION ALL
SELECT 1, 'def', GETDATE()-2
UNION ALL
SELECT 1, 'hij', GETDATE()-2
UNION ALL
SELECT 1, 'abc', GETDATE()-4
UNION ALL
SELECT 1, 'def', GETDATE()-4
UNION ALL
SELECT 1, 'hij', GETDATE()-4
UNION ALL
SELECT 2, 'abc', GETDATE()-1
UNION ALL
SELECT 2, 'def', GETDATE()-1
UNION ALL
SELECT 2, 'hij', GETDATE()-1
UNION ALL
SELECT 2, 'abc', GETDATE()-3
UNION ALL
SELECT 2, 'def', GETDATE()-3
UNION ALL
SELECT 2, 'hij', GETDATE()-3
UNION ALL
SELECT 2, 'abc', GETDATE()-4
UNION ALL
SELECT 2, 'def', GETDATE()-4
UNION ALL
SELECT 2, 'hij', GETDATE()-4
UNION ALL
SELECT 3, 'abc', GETDATE()+2
UNION ALL
SELECT 3, 'def', GETDATE()+2
UNION ALL
SELECT 3, 'hij', GETDATE()+2
UNION ALL
SELECT 3, 'abc', GETDATE()-4
UNION ALL
SELECT 3, 'def', GETDATE()-4
UNION ALL
SELECT 3, 'hij', GETDATE()-4
UNION ALL
SELECT 3, 'abc', GETDATE()-5
UNION ALL
SELECT 3, 'def', GETDATE()-5
UNION ALL
SELECT 3, 'hij', GETDATE()-5
UNION ALL
SELECT 4, 'abc', GETDATE()+1
UNION ALL
SELECT 4, 'def', GETDATE()+1
UNION ALL
SELECT 4, 'hij', GETDATE()+1
UNION ALL
SELECT 4, 'abc', GETDATE()-4
UNION ALL
SELECT 4, 'def', GETDATE()-4
UNION ALL
SELECT 4, 'hij', GETDATE()-4
UNION ALL
SELECT 4, 'abc', GETDATE()-5
UNION ALL
SELECT 4, 'def', GETDATE()-5
UNION ALL
SELECT 4, 'hij', GETDATE()-5
SELECT * FROM #TABLE
The data output that I want reflects output of the following records
3 abc 2013-09-18
3 def 2013-09-18
3 hij 2013-09-18
Description: I would like to query the data for the ID/Name that was processed most recently (Max(Date)) (there can be multiple ID's if they were processed at the same date/time)
My try...
SELECT DISTINCT A.*, B.My_Rank
FROM #TABLE A
INNER JOIN
(
SELECT DISTINCT ID, Name, CONVERT(varchar(8), Date, 101) AS Date, RANK() OVER (PARTITION BY ID, Name ORDER BY CONVERT(varchar(8), Date, 101) DESC) My_Rank, MAX(CONVERT(varchar(8), Date, 101)) OVER (partition by ID, Name) Max_Date
FROM #TABLE
GROUP BY ID, Name, CONVERT(varchar(8), Date, 101)
) B
ON A.ID = B.ID AND CONVERT(varchar(8), A.Date, 101) = B.Max_Date
WHERE My_Rank = 1
Clearly, this logic is not working out. "I want to get records for ID/Name/Date based on latest processed ID/Name.
Thank you
You were close, but you don't have to join back (and you probably don't want the PARTITION BY id:
SELECT *
FROM
(
SELECT ID, Name, Date,
RANK() OVER (ORDER BY CAST(Date AS DATE) DESC) My_Rank
FROM #TABLE
) dt
WHERE My_rank = 1
AFAIK SS2008 supports CAST(x AS DATE)
You want to use max() with the partition by clause:
SELECT *
FROM (select a.*,
max("date") over (partition by id) as maxdate
from #TABLE A
) a
where "date" = maxdate;
This can return multiple rows. If you only one one, even when there are ties, then use row_number():
SELECT *
FROM (select a.*,
row_number() over (partition by id order by "date" desc) as seqnum
from #TABLE A
) a
where seqnum = 1;

Concatenating in one column based on another column

I'm working with SQL Server 2008.
Let's say I have these three tables:
Product
1, "Honda Civic"
2, "Fiat Ritmo"
3, "Ford Fiesta"
ProductGroup
1, 1, 1
2, 1, 2
3, 3, 1
4, 2, 3
5, 2, 4
6, 1, 5
Group
1 American
2 Asian
3 European
4 Old
5 Fast
And let's say I want to query these tables so as to get the following:
"Fiat Ritmo", "European, Old"
"Honda Civic", "Asian, Fast"
"Ford Focus", "American"
How can I do this?
I'm using paging on the client side so I can't group the pages there.
Thanks for your help,
Barry
Something like this:
First some test data:
DECLARE #Product TABLE(ID INT, Name VARCHAR(100))
INSERT INTO #Product
SELECT 1,'Honda Civic' UNION ALL
SELECT 2,'Fiat Ritmo' UNION ALL
SELECT 3,'Ford Fiesta'
DECLARE #ProductGroup TABLE(ID INT,ProductID INT, GroupID INT)
INSERT INTO #ProductGroup
SELECT 1, 1, 1 UNION ALL
SELECT 2, 1, 2 UNION ALL
SELECT 3, 3, 1 UNION ALL
SELECT 4, 2, 3 UNION ALL
SELECT 5, 2, 4 UNION ALL
SELECT 6, 1, 5
DECLARE #Group TABLE(ID INT, Name VARCHAR(100))
INSERT INTO #Group
SELECT 1, 'American' UNION ALL
SELECT 2, 'Asian' UNION ALL
SELECT 3, 'European' UNION ALL
SELECT 4, 'Old' UNION ALL
SELECT 5, 'Fast'
Then the query:
SELECT
Product.ID,
Product.Name,
STUFF
(
(
SELECT
',' + Groups.Name
FROM
#ProductGroup AS ProductGroup
JOIN #Group AS Groups
ON ProductGroup.GroupID=Groups.ID
WHERE
ProductGroup.ProductID = Product.ID
FOR XML PATH('')
),1,1,'') AS Names
FROM
#Product AS Product
Use FOR XML PATH feature to get string grouping like this:
DECLARE #Product TABLE (Id INT PRIMARY KEY, Name NVARCHAR(100) NOT NULL)
INSERT #Product
VALUES(1, 'Honda Civic'), (2, 'Fiat Ritmo'), (3, 'Ford Fiesta')
DECLARE #ProductGroup TABLE(Product INT, Grp INT, PRIMARY KEY(Product, Grp))
INSERT #ProductGroup
VALUES
(1, 1),
(1, 2),
(3, 1),
(2, 3),
(2, 4),
(1, 5)
DECLARE #Grp TABLE(Id INT PRIMARY KEY, NAME NVARCHAR(100))
INSERT #Grp
VALUES
(1, 'American'),
(2, 'Asian'),
(3, 'European'),
(4, 'Old'),
(5, 'Fast')
SELECT p.name,
(
SELECT Name+','
FROM #ProductGroup pg
JOIN #Grp g
ON pg.Grp = g.Id
WHERE pg.Product = p.Id
FOR XML PATH('')
) Groups
FROM #Product p