query to print users who have done purchase on more than 1 day and products purchased on a given day are never repeated on any other day - input

Given the users purchase history write a query to print users who have done purchase on more than 1 day and products purchased on a given day are never repeated on any other day.
Here is the ready script:
create table purchase_history
(userid int
,productid int
,purchasedate date
);
SET DATEFORMAT dmy;
insert into purchase_history values
(1,1,'23-01-2012')
,(1,2,'23-01-2012')
,(1,3,'25-01-2012')
,(2,1,'23-01-2012')
,(2,2,'23-01-2012')
,(2,2,'25-01-2012')
,(2,4,'25-01-2012')
,(3,4,'23-01-2012')
,(3,1,'23-01-2012')
,(4,1,'23-01-2012')
,(4,2,'25-01-2012');
I want to see one column i,e userid with data 1 and 4

Related

Selecting a Max and Min from the same column in SQL in a single query

I have a table that contains entries for each step in the supplier delivery process.
For example: arrived, on-premise, offloaded, and off-premise.
When each of these steps happens, we capture the timestamp it happened on.
I want to select the duration it was on-premise for all the suppliers that have a status ID bigger or equal to 30. in other words, the biggest status ID - the smallest status ID Where the smallest Status ID >= 30
Note that not all the steps are necessarily completed, I need to select the highest step for that supplier.
How do I do this in SQL?
My supplier delivery history table's columns:
GUID ID,
DATETIME TimeStamp,
GUID FK_SupplierDeliveryID,
TINYINT FK_SupplierDeliveryStatusID
Supplier delivery status table's columns:
TINYINT ID,
NVARCHAR Description,
Supplier table:
GUID ID,
NVARCHAR SupplierName
Ideally, I would like to return the following fields from the query:
SupplierID, SupplierName, LastStatus, Time In, Time Out, Elapse
where Supplier ID is the ID of the supplier table, Supplier Name is the description of the supplier table, LastStatus is the biggest StatusKey captured for the supplier, Time In is the date of the entry where the StatusKey = 30, Time Out is the date of the entry of the biggest StatusKey captured for the supplier is = 40 else null, and Elapse = Time Out - Time In
I have tried:
SELECT
sdh.FK_SupplierDeliveryID,
MAX(sdh.StatusKey) AS HighestStatus,
MIN(sdh.StatusKey) AS LowestStatus,
MAX(sdh.StatusDate) AS HighestDate,
MIN(sdh.StatusDate) AS LowestDate
FROM
SupplierDeliveryStatusHistory AS sdh
WHERE
sdh.StatusKey> 30
GROUP BY
sdh.FK_SupplierDeliveryID,
sdh.StatusKey
You're aggregating on the sdh.StatusKey column, so you shouldn't group by it:
SELECT
sdh.FK_SupplierDeliveryID,
MAX(sdh.StatusKey) AS HighestStatus,
MIN(sdh.StatusKey) AS LowestStatus
FROM
SupplierDeliveryStatusHistory AS sdh
WHERE
sdh.StatusKey> 30
GROUP BY
sdh.FK_SupplierDeliveryID -- sdh.StatusKey removed here

Most recurring weekday of a datetime2 fiel on a Table

I have a Table with Orders. On that table I have a field CreatedAt (datetime2). Is it possible to create a select query that shows me how many orders there have been made on every weekday? Not between to dates but over the hole table.
Like:
Monday:52
Tuesday: 16
...
and so on for all 7 days in a week.
Try this:
select datepart(weekday,CreatedAt) weekday,count(*) count
from Table
group by datepart(weekday,CreatedAt)

SQL Server - Insert with multiple sub queries (3 tables)

I have a table called BankHoliday which contains a Date field along with the Id field.. This currently has all of the dates for this year's and next year's Bank Holidays.
I also have a table called Staff which contains StaffID, StaffName, Active
Now using SQL I am trying to write an insert statement into a table called Calendar in which these Bank Holiday Dates are inserted. This table contains CalendarDate, CalendarID and StaffID,
The StaffID is where my problem is...
The Question
How can I write an INSERT statement in SQL Server where it will fetch a list of the Active Staff Members along with each individual Bank Holiday Date, and then INSERT them into the Calendar table?
Example Data would return:
CalendarID CalendarDate StaffID
1 31/08/2015 1
2 31/08/2015 2
3 31/08/2015 3
4 31/08/2015 4
5 31/08/2015 5
6 25/12/2015 1
7 25/12/2015 2
8 25/12/2015 3
9 25/12/2015 4
10 25/12/2015 5
So to clarify the CalendarDate above will contain a list of BankHoliday Dates that are not currently in this table (WHERE NOT EXISITS)
The StaffID is retrieved from the Staff table where a list contains only Active members
I think you are looking for a CROSS JOIN.
INSERT INTO Calendar
SELECT b.CalendarDate, s.StaffID FROM BankHoliday b
CROSS JOIN Staff s
WHERE s.Active = 1
This will get a row for every bank holiday and every active staff member.
Ther is an accepted answer already, so I'm to late - never mind. Just one hint: I think you could improve the design:
You should create a date-table not only for bank holidays, but a
running list of all dates. These dates you can mark as
"IsBankHoliday". Such a date list is very usefull in many cases.
I don't know what you are going to do with this, but normally you
would not write data into a physical table, which you can retrieve
that easily. So - probably! - it's not the best idea to do this...
Just my solution to show you the connection of the tables. In fact this is nothing else then the other solution with CROSS JOIN... Person4 is not active, therefore is missing:
declare #bankholidays TABLE(ID INT,TheDate DATE);
insert into #bankholidays values(1,{ts'2015-08-31 00:00:00'}),(2,{ts'2015-12-25 00:00:00'});
declare #staff TABLE(StaffID INT,StaffName VARCHAR(100could),Active BIT);
insert into #staff VALUES(1,'Person1',1),(2,'Person2',1),(3,'Person3',1),(4,'Person4',0),(5,'Person5',1);
declare #target TABLE(targetID INT,CalendarID INT,CalendarDate DATE,StaffID INT);
INSERT INTO #target
SELECT ROW_NUMBER() OVER(ORDER BY bh.TheDate,s.StaffID)
,bh.ID
,bh.TheDate
,s.StaffID
FROM #staff AS s,#bankholidays AS bh
WHERE s.Active=1;
SELECT * FROM #target

SQL getting count in a date range

I'm looking for input on getting a COUNT of records that were 'active' in a certain date range.
CREATE TABLE member {
id int identity,
name varchar,
active bit
}
The scenario is one where "members" number fluctuate over time. So I could have linear growth where I have 10 members at the beginning of the month and 20 at the end. Currently We go off the number of CURRENTLY ACTIVE (as marked by an 'active' flag in the DB) AT THE TIME OF REPORT. - this is hardly accurate and worse, 6 months from now, my "members" figure may be substantially different than now. and Since I'm doing averages per user, if I run a report now, and 6 months from now - the figures will probably be different.
I don't think a simple "dateActive" and "dateInactive" will do the trick... due to members coming and going and coming back etc. so:
JOE may be active 12-1 and deactivated 12-8 and activated 12-20
so JOE counts as being a 'member' for 8 days and then 11 days for a total of 19 days
but the revolving door status of members means keeping a separate table (presumably) of UserId, status, date
CREATE TABLE memberstatus {
member_id int,
status bit, -- 0 for in-active, 1 for active
date date
} (adding this table would make the 'active' field in members obsolete).
In order to get a "good" Average members per month (or date range) - it seems I'd need to get a daily average, and do an average of averages over 'x' days. OR is there some way in SQL to do this already.
This extra "status" table would allow an accurate count going back in time. So in a case where you have a revenue or cost figure, that DOESN'T change or is not aggregate, it's fixed, that when you want cost/members for last June, you certainly don't want to use your current members count, you want last Junes.
Is this how it's done? I know it's one way, but it the 'better' way...
#gordon - I got ya, but I guess I was looking at records like this:
Members
1 Joe
2 Tom
3 Sue
MemberStatus
1 1 '12-01-2014'
1 0 '12-08-2014'
1 1 '12-20-2014'
In this way I only need the last record for a user to get their current status, but I can track back and "know" their status on any give day.
IF I'm understanding your method it might look like this
CREATE TABLE memberstatus {
member_id int,
active_date,
inactive_date
}
so on the 1-7th the record would look like this
1 '12-01-2014' null
and on the 8th it would change to
1 '12-01-2014' '12-08-2014'
the on the 20th
1 '12-01-2014' '12-08-2014'
1 '12-20-2014' null
Although I can get the same data out, it seems more difficult without any benefit - am i missing something?
You could also use a 2 table method to have a one-to-many relationship for working periods. For example you have a User table
User
UserID int, UserName varchar
and an Activity table that holds ranges
Activity
ActivityID int, UserID int, startDate date, (duration int or endDate date)
Then whenever you wanted information you could do something like (for example)...
SELECT User.UserName, count(*) from Activity
LEFT OUTER JOIN User ON User.UserID = Activity.UserID
WHERE startDate >= '2014-01-01' AND startDate < '2015-01-01'
GROUP BY User.UserID, User.UserName
...to get a count grouped by user (and labeled by username) of the times they were became active in 2014
I have used two main ways to accomplish what you want. First would be something like this:
CREATE TABLE [MemberStatus](
[MemberID] [int] NOT NULL,
[ActiveBeginDate] [date] NOT NULL,
[ActiveEndDate] [date] NULL,
CONSTRAINT [PK_MemberStatus] PRIMARY KEY CLUSTERED
(
[MemberID] ASC,
[ActiveBeginDate] ASC
)
Every time a member becomes active, you add an entry, and when they become inactive you update their ActiveEndDate to the current date.
This is easy to maintain, but can be hard to query. Another option is to do basically what you are suggesting. You can create a scheduled job to run at the end of each day to add entries to the table .
I recommend setting up your tables so that you store more data, but in exchange the structure supports much simpler queries to achieve the reporting you require.
-- whenever a user's status changes, we update this table with the new "active"
-- bit, and we set "activeLastModified" to today.
CREATE TABLE member {
id int identity,
name varchar,
active bit,
activeLastModified date
}
-- whenever a user's status changes, we insert a new record here
-- with "startDate" set to the current "activeLastModified" field in member,
-- and "endDate" set to today (date of status change).
CREATE TABLE memberStatusHistory {
member_id int,
status bit, -- 0 for in-active, 1 for active
startDate date,
endDate date,
days int
}
As for the report you're trying to create (average # of actives in a given month), I think you need yet another table. Pure SQL can't calculate that based on these table definitions. Pulling that data from these tables is possible, but it requires programming.
If you ran something like this once-per-day and stored it in a table, then it would be easy to calculate weekly, monthly and yearly averages:
INSERT INTO myStatsTable (date, activeSum, inactiveSum)
SELECT
GETDATE(), -- based on DBMS, eg., "current_date" for Postgres
active.count,
inactive.count
FROM
(SELECT COUNT(id) FROM member WHERE active = true) active
CROSS JOIN
(SELECT COUNT(id) FROM member WHERE active = true) inactive

How to link database rows to and fro?

I have a query on database table structure.
My Patient Details table in a SQL Server 2008 database looks like this:
--PatientId(PK)-- --PatientType-- --DoctorId(FK)-- --DateOfVisit-- --PrescriptionId(FK)--
Each patient may visit the hospital multiple times. I need to link all the visits of a patient and show them in a linked way that the admin can navigate with the previous and next visits..
So I think I can do it only by having a record for each visit with a VisitId.
I need to be Careful that the database should not be loaded with unnecessary fields. Also it should not affect the fetching time badly.
I think these are the available ways. Suggest me if any available.
--PatientId(PK)-- --PatientType-- --DoctorId(FK)-- --DateOfVisit-- --PrescriptionId(FK)-- --VisitId-- --PrevVisitId-- NextVisitId--
or
having a separate table for Visit as
--VisitId(PK)-- -PrevVisitId(FK)-- --NextVisitId(FK)--
Apologies if my post is duplicate.. Correct me, redirect me wherever/whenever needed.
So I think I can do it only by having a record for each visit with a
VisitId.
You need a row for each visit. You need a key for each row. The key doesn't need to be a single id number. I think the very least you need to record for each visit is
create table visits (
patient_id integer not null, -- references patients (patient_id), not shown
doctor_id integer not null, -- references doctors (doctor_id), not shown
office_visit_start timestamp not null default current_timestamp,
primary key (patient_id, office_visit_start)
);
insert into visits values (1, 1, '2012-02-01 08:00');
insert into visits values (1, 1, '2012-02-01 15:00');
insert into visits values (1, 1, '2012-03-01 09:33');
insert into visits values (2, 1, '2012-02-01 09:00');
(Exact syntax varies depending on your dbms platform.) You can find the previous and next visits by fairly simple queries--"office_visit_start" will give you the order of a patient's visits.
Example queries . . .
-- Previous visit for patient # 1 (before 2012-02-01 15:00)
select patient_id, max(office_visit_start)
from visits
where patient_id = 1
and office_visit_start < '2012-02-01 15:00'
group by patient_id
-- Next visit for patient # 1 (after 2012-02-01 15:00)
select patient_id, min(office_visit_start)
from visits
where patient_id = 1
and office_visit_start > '2012-02-01 15:00'
group by patient_id