Best approach to merge three distinct TSQL select statments - sql-server

I'm looking to return one tsql statement, that contains four fields, from three separate, unrelated tables.
One table contains a list of objects, say fruits, and for each fruit, I want a sell buy date and best before date.
First statement would therefore look something like:
select fruit from fruit table -- this returns multiple rows
Second statement would look something like:
select sellbuyDate from sellTable -- this returns a single row
and the third would look something like:
select bestbefore from bestTable -- this returns a single row
Don't get to hung up on the table names. I'm working on a legacy system, that we cant change, so need to combine the three table into one.
The underlining table needs to have all the fields returned in a single row, with the second and third results applied to the first statement.
Apples | 12-12-2008 | 12-12-2009
Pears | 12-12-2008 | 12-12-2009
I've implemented the following temp table:
CREATE TABLE #Fruits
(
Fruit VARCHAR(100),
SellBuyDate DATETIME,
BestBefore DATETIME
)
INSERT INTO #Fruits
SELECT Fruit from fruit
INSERT INTO #Fruits
SELECT sellbuyDate from sellTable
INSERT INTO #Fruits
SELECT bestbefore from bestable
SELECT * from #Fruits
This throws an error, because each insert doesn't contain the three fields specified.
any other suggestions would be well received.

You can select them all together by doing a CROSS JOIN by not specifying any join criteria between the three tables as follows:
CREATE TABLE fruit ( fruit_name VARCHAR(30) );
CREATE TABLE sellTable ( sellByDate DATETIME );
CREATE TABLE bestTable ( bestBefore DATETIME );
CREATE TABLE allFruits
(
fruit_name VARCHAR(30),
sellByDate DATETIME,
bestBefore DATETIME
);
INSERT INTO fruit (fruit_name)
VALUES ('apple'), ('pear');
INSERT INTO sellTable(sellByDate)
VALUES ('12/05/2012');
INSERT INTO bestTable(bestBefore)
VALUES ('12/12/2012');
INSERT INTO allFruits (fruit_name, bestBefore, sellByDate)
SELECT f.fruit_name, b.bestBefore, s.sellByDate
FROM fruit f, bestTable b, sellTable s;
SELECT *
FROM allFruits;

maybe you are looking for this answer, although your question could be a little clearer on what that would accomplish
INSERT INTO #Fruits(fruit)
SELECT Fruit from fruit
INSERT INTO #Fruits(sellbuyDate)
SELECT sellbuyDate from sellTable
INSERT INTO #Fruits(bestbefore)
SELECT bestbefore from bestable
SELECT * from #Fruits
the other possible solution is
insert into #Fruits
select Fruit, sellbuyDate, bestbefore from fruit
cross join sellTable
cross join bestable

May be you need this.
SELECT FRUIT,
(SELECT SELLBUYDATE FROM SELLTABLE) AS SELLBUYDATE,
(SELECT BESTBEFORE FROM BESTTABLE) AS BESTBEFORE
FROM FRUIT
or
SELECT FRUIT.FRUIT
, SELLTABLE.SELLBUYDATE
, BESTTABLE.BESTBEFORE
FROM FRUIT
INNER JOIN SELLTABLE ON 1=1
INNER JOIN BESTTABLE ON 1=1

Without the schema of the 3 tables. I would guess its one of the following.
CREATE TABLE #Fruits
(
Fruit VARCHAR(100),
SellBuyDate DATETIME,
BestBefore DATETIME
)
INSERT INTO #Fruits(Fruit,SellBuyDate,BestBefore)
Select Fruit, Sellbuydate,bestbefore
from fruit,selltable,bestable
Or if your tables are set up properly
INSERT INTO #Fruits(Fruit,SellBuyDate,BestBefore)
Select Fruit, Sellbuydate,bestbefore
from fruit f
inner join selltable s
on f.pkey = s.fkey
inner join bestable b
on f.pkey = b.fkey

Related

How is this SQL sub-query correctly vectorising?

Sample data:
CREATE TABLE Departments (
Code INTEGER PRIMARY KEY,
Name varchar(255) NOT NULL ,
Budget decimal NOT NULL
);
CREATE TABLE Employees (
SSN INTEGER PRIMARY KEY,
Name varchar(255) NOT NULL ,
LastName varchar(255) NOT NULL ,
Department INTEGER NOT NULL ,
foreign key (department) references Departments(Code)
)
INSERT INTO Departments(Code,Name,Budget) VALUES(14,'IT',65000);
INSERT INTO Departments(Code,Name,Budget) VALUES(37,'Accounting',15000);
INSERT INTO Departments(Code,Name,Budget) VALUES(59,'Human Resources',240000);
INSERT INTO Departments(Code,Name,Budget) VALUES(77,'Research',55000);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('123234877','Michael','Rogers',14);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('152934485','Anand','Manikutty',14);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('222364883','Carol','Smith',37);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('326587417','Joe','Stevens',37);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('332154719','Mary-Anne','Foster',14);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('332569843','George','O''Donnell',77);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('546523478','John','Doe',59);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('631231482','David','Smith',77);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('654873219','Zacary','Efron',59);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('745685214','Eric','Goldsmith',59);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('845657245','Elizabeth','Doe',14);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('845657246','Kumar','Swamy',14);
Problem: "Select the names of departments with more than two employees."
Wikibooks solution:
/*With subquery*/
SELECT D.Name FROM Departments D
WHERE 2 <
(
SELECT COUNT(*)
FROM Employees
WHERE Department = D.Code
);
My question: How does this solution work? That is, how does MSSQL know which values in Departments are to be kept from the sub-query? I can't see any way that the condition WHERE Department = D.Code can return a result that is ordered in a useful way to the outer query. I don't think that this is a fluke, I think that I just don't understand how SQL is vectorised.
This is called a correlated subquery.
That is to say, the inner query is correlated to the outer one by use of an outer reference. In this case, that is D.Code. Therefore the subquery is being calculated for every row of D.
It's not a matter of ordering, in fact this query can return results in any order. But the result from the subquery must be greater than 2 otherwise the WHERE predicate fails.
SELECT D.Name FROM Departments D -- Departments has been aliased as D
WHERE 2 <
(
SELECT COUNT(*)
FROM Employees
WHERE Department = D.Code -- Here the inner query is being limited by
-- the reference to the outer D table
);
I would probably use ... > 2 rather than 2 < ...
Side point: It's better to always use an explicit table reference in subqueries, eg e.Department = D.Code, because otherwise you could misspell a column and end up referring to an outer column instead of an inner column, and the correlation wouldn't work properly

Show all and only rows in table 1 not in table 2 (using multiple columns)

I have one table (Table1) that has several columns used in combination: Name, TestName, DevName, Dept. When each of these 4 columns have values, the record is inserted into Table2. I need to confirm that all of the records with existing values in each of these fields within Table1 were correctly copied into Table 2.
I have created a query for it:
SELECT DISTINCT wr.Name,wr.TestName, wr.DEVName ,wr.Dept
FROM table2 wr
where NOT EXISTS (
SELECT NULL
FROM TABLE1 ym
WHERE ym.Name = wr.Name
AND ym.TestName = wr. TestName
AND ym.DEVName = wr.DEVName
AND ym. Dept = wr. Dept
)
My counts are not adding up, so I believe that this is incorrect. Can you advise me on the best way to write this query for my needs?
You can use the EXCEPT set operator for this one if the table definitions are identical.
SELECT DISTINCT ym.Name, ym.TestName, ym.DEVName, ym.Dept
FROM table1 ym
EXCEPT
SELECT DISTINCT wr.Name, wr.TestName, wr.DEVName, wr.Dept
FROM table2 wr
This returns distinct rows from the first table where there is not a match in the second table. Read more about EXCEPT and INTERSECT here: https://learn.microsoft.com/en-us/sql/t-sql/language-elements/set-operators-except-and-intersect-transact-sql?view=sql-server-2017
Your query should do the job. It checks anything that are in Table1, but not Table2
SELECT ym.Name, ym.TestName, ym.DEVName, ym.Dept
FROM Table1 ym
WHERE NOT EXISTS (
SELECT 1
FROM table2
WHERE ym.Name = Name AND ym.TestName = TestName AND ym.DEVName = DEVName AND ym. Dept = Dept
)
If the structure of both tables are the same, EXCEPT is probably simpler.
IF OBJECT_ID(N'tempdb..#table1') IS NOT NULL drop table #table1
IF OBJECT_ID(N'tempdb..#table2') IS NOT NULL drop table #table2
create table #table1 (id int, value varchar(10))
create table #table2 (id int)
insert into #table1(id, value) VALUES (1,'value1'), (2,'value2'), (3,'value3')
--test here. Comment next line
insert into #table2(id) VALUES (1) --Comment/Uncomment
select * from #table1
select * from #table2
select #table1.*
from #table1
left JOIN #table2 on
#table1.id = #table2.id
where (#table2.id is not null or not exists (select * from #table2))

SQL Grouping and Counting based on dates

Getting nowhere here and seems so simple.
Test data is:
declare #table table(SpellAdminsionDate datetime, SpellDischargeDate datetime, Pat_code varchar(10))
insert into #table (SpellAdminsionDate, SpellDischargeDate, Pat_code) values('2016-09-12 15:55:00:000','2016-09-19 20:20:00:000','HEY3052275')
insert into #table(SpellAdminsionDate, SpellDischargeDate, Pat_code) values ('2016-09-07 17:17:00:000','2016-09-17 18:40:00:000','HEY0810155')
insert into #table(SpellAdminsionDate, SpellDischargeDate, Pat_code) values ('2016-09-14 16:50:00:000','2016-09-17 18:01:00:000','HEY1059266')
insert into #table(SpellAdminsionDate, SpellDischargeDate, Pat_code) values ('2016-09-15 02:47:00:000','2016-09-15 17:28:00:000','HEY0742883')
insert into #table(SpellAdminsionDate, SpellDischargeDate, Pat_code) values ('2016-08-27 00:11:00:000','2016-09-14 12:49:00:000','HEY3050628')
insert into #table(SpellAdminsionDate, SpellDischargeDate, Pat_code) values ('2016-09-10 12:24:00:000','2016-09-13 20:00:00:000','HEY0912392')
insert into #table(SpellAdminsionDate, SpellDischargeDate, Pat_code) values ('2016-09-12 12:51:00:000','2016-09-13 19:55:00:000','HEY0908691')
Select * from #table`
Below is my simple code displaying the same thing:
SELECT c.SpellAdmissionDate,
c.SpellDischargeDate,
c.Pat_Code
FROM [CommDB].[dbo].[vwCivicaSLAM1617Live] c
WHERE c.Hrg_Code like 'VA%'
and c.Pat_Code like 'HEY%'
ORDER BY c.SpellDischargeDate desc
All I am after is a COUNT per day of active patients, for example take the 12/09/2016 on that date the result would be 5 (based on the test data) as the other 2 cam in after the 12th.
If it makes it easier I do have a date reference table called DATE_REFERENCE which has every date available to me.
Allowing for the possibility of having no patients on a day then you want to use your date reference table as the primary and left join to the patient information. You don't identify a column name so I just used [datecol].
SELECT
d.[datecol] the_date
, count(DISTINCT c.Pat_Code) num_patients
FROM DATE_REFERENCE d
LEFT JOIN [CommDB].[dbo].[vwCivicaSLAM1617Live] c
ON d.[datecol] BETWEEN c.SpellAdmissionDate AND c.SpellDischargeDate
AND c.Hrg_Code LIKE 'VA%'
AND c.Pat_Code LIKE 'HEY%'
GROUP BY
d.[datecol]
ORDER BY
d.[datecol] DESC
I suspect there may be more than this required, but without sample data and expected results it is difficult to know what you really need.
nb. I assume the date in that date_reference table is at midnight (00:00:00) or has a data type of date (without hours/minutes etc.)
Is this what you want?
SELECT dr.date,
(SELECT COUNT(*)
FROM [CommDB].[dbo].[vwCivicaSLAM1617Live] c
WHERE dr.date between c.SpellAdmissionDate and c.SpellDischargeDate) as cnt_per_day
FROM DATE_REFERENCE dr
Add the filters you want to the correlated query .
You can achieve this by joining to your date reference table and counting by a distinct of the patient reference
SELECT
dateRef
,COUNT(DISTINCT Pat_Code) AS PatCount
FROM #table t
RIGHT JOIN #date_reference
ON SpellAdminsionDate <= dateRef
AND SpellDischargeDate >= dateRef
GROUP BY dateRef;
Active count of patients per date can be achieved by (for temp data provided here)
SELECT DISTINCT CAST(T1.SPELLADMINSIONDATE AS DATE)
AdmissionDate,PATIENTS.TotalActive
FROM #TABLE T1
CROSS APPLY (SELECT COUNT(*)TotalActive FROM #TABLE T2 WHERE
CAST(T2.SPELLADMINSIONDATE AS DATE)<=CAST(T1.SPELLADMINSIONDATE AS DATE)) PATIENTS
I think you need to bring the DATE_REFERENCE table to the Query If it contains all the day dates that you need to count patients based on them, But here is a sample of how may you get the required result form this table only
Select DISTINCT c.SpellAdmissionDate, count(c.Pat_code) as PCounter
From [CommDB].[dbo].[vwCivicaSLAM1617Live] c
GROUP BY c.SpellAdmissionDate
ORDER BY c.SpellAdmissionDate desc

SQL - How To Return A Result Without Multiples

I am trying to write a Business Object report to show a list of the people who have not returned a timesheet on a selected date, but I can't figure out how to stop the SQL query returning multiple entries for individuals.
My Staff_Table contains 2 columns - Employee No & Name
My Timesheet_Table contains, among other things, Employee No & Week_Ending_Date.
I can easily write a statement to return all users who have entered a timesheet with a Week_Ending_Date of e.g. 10/08/2012. However, if I try to return a list of all those who have not enetered a timesheet for 10/08/2012, I pick up every single timesheet in the table which does not have that date, so, for example, if a person has submitted 100 timesheets and only 1 of them is for 10/08/2012, the results will show him 99 times.
What I need is a fixed list of everyone on the Staff_Table who has not submitted for that date, showing once only.
I tried a Union with NOT EXISTS but either I'm doing it wrong or it simply isn't appropriate.
Can anyone point me in the right direction?
You should select all employee numbers that do not enter timesheet first. Then, filter the list using NOT IN.
DECLARE #Week_Ending_Date DATETIME = '2012-08-10'
DECLARE #Staff TABLE
(
EmployeeNo INT NOT NULL,
EmployeeName NVARCHAR(100) NOT NULL
)
DECLARE #TimeSheet TABLE
(
EmployeeNo INT NOT NULL,
Week_Ending_Date DATETIME
)
INSERT INTO #Staff (EmployeeNo, EmployeeName)
VALUES (1, 'Alan'), (2, 'Peter')
INSERT INTO #TimeSheet (EmployeeNo, Week_Ending_Date)
VALUES (1, '2012-08-10'), (1, '2012-08-17'), (2, '2012-08-03')
SELECT
S.EmployeeName
FROM
#Staff S
WHERE
EmployeeNo NOT IN (SELECT EmployeeNo FROM #TimeSheet WHERE Week_Ending_Date = #Week_Ending_Date)
Try adding DISTINCT to your query
ie
SELECT DISTINCT ...
Your query should look something like
SELECT *
FROM Staff
WHERE NOT EXISTS
(
Select EmployeeNo
from Timesheet
where WeekEndingDate='2012-08-10'
and TimeSheet.EmployeeNo = Staff.EmployeeNo
)
or
SELECT *
FROM Staff
WHERE EmployeeNo NOT IN
(
Select EmployeeNo
from Timesheet
where WeekEndingDate='2012-08-10'
)
You could use a not exists clause to find staff that has not submitted a particular timesheet:
select *
from Staff s
where not exists
(
select *
from Timesheet t
where t.EmployeeNo = s.EmployeeNo
and t.Week_Ending_Date = '2012-08-19'
)
Can you not just select all employees who haven't submitted a timesheet and group the results by their name?
select Name
from Staff_Table
left join Timesheet_Table on Staff_Table.[Employee No] = Timesheet_Table.[Employee No] and Timesheet_Table.Week_Ending_Date = '10 August 2012'
having Timesheet_Table.Week_Ending_Date is null
group by Name, Timesheet_Table.Week_Ending_Date
I haven't tested this, but something along these lines.

Convert sql subquery to list or varchar

I want to know if we can convert a subquery result to comma separated list in varchar datatype.
For eg.
If I have a product table. And I have product image table with foreign key of product.
now I want to list all product with select query that should have a column with list of productImage table's pk list for each product.
I'm using sql server 2005. Can we achieve the above in any way?
Select p.ProductID,
Stuff((Select ','+Cast(ImageID as varchar(10))
From #ProductImages i
Where p.ProductID=i.ProductId
For XML PATH('')
),1,1,''
) as ImageList
From #Products p
Where p.ProductID in (Select ProductID From #ProductImages)
Here are test data I used for this query
Declare #Products Table (ProductID int primary key, ProductName varchar(20))
Declare #ProductImages Table (ProductId int, ImageId int, Primary Key (ProductId, ImageId))
Insert Into #Products
Select 1, 'Product1' Union all
Select 2, 'Product1' Union all
Select 3, 'Product1' Union all
Select 4, 'Product1' Union all
Select 5, 'Product1'
Insert Into #ProductImages
Select 1,1 Union all
Select 1,2 Union all
Select 1,3 Union all
Select 2,4 Union all
Select 2,5 Union all
Select 3,1 Union all
Select 4,3 Union all
Select 4,5
And here is result of query:
ProductID ImageList
--------- ---------
1 1,2,3
2 4,5
3 1
4 3,5
If you want to have ProductID 5 in the list with null for Image list, just remove next line from query:
Where p.ProductID in (Select ProductID From #ProductImages)
You will have one more row in the result (it does not have images assigned):
5 null
I'm not sure if I'm following you, but maybe you can try to add an extra table:
Table Product
Table Images
Table Product_Images.
In this last table you'll have at least 3 columns, PK for Product_Images table, Product_FK and
Images_FK. Then with just
SELECT Image_FK FROM Product_Images WHERE Product_Images.Product_FK = ##;
you'll have a list of Images' PK associated with a product
Hope this will help you,
regards.
Not easily, no. I've found the best way to do these things is to execute the subquery on its own, and assemble the comma-separated list programmatically.
If you really need to do it on the database, you could define a scalar-valued function that you can give the value for the foreign key, and which returns the comma-separated list. You'll have to use a cursor inside that function to make the magic happen though.

Resources