SQL Query need to show blank records - sql-server

I have a query where i'm selecting 50 ids from the DB and grouping them by the id.
The only problem is that only 34 exists in the DB so it only shows 34 results.
My desired result is to show 50 results with the ones that don't exists in.
Is this possible?
Thanks
My query
SELECT no, count(no) as visits FROM DB
WHERE (no = '1' or no = '2' or no = '3' or no = '4' or no = '5')
GROUP BY no
This example will only give me 3 records even though i'm selecting 5 records

with noTable
as
( select 1 as no
union
select 2
union
select 3
union
select 4
union
select 5
)
select noTable.no,count(*)
from noTable inner join DB
on noTable.no = DB.no group by noTable.no
union
select noTable.no,0
from noTable left join DB
on noTable.no = DB.no
where DB.no is null;
Might not be elegant if there're 50 id...

You could add a tally table using WITH and LEFT JOIN your original table with the tally table.
;with q (no) AS (
SELECT 1
UNION ALL
SELECT no + 1
FROM q
WHERE no < 50
)
SELECT q.no
, CASE WHEN db.no IS NOT NULL
THEN COUNT(q.no)
ELSE 0
END AS Visits
FROM q
LEFT JOIN db ON db.no = q.no
GROUP BY
no

Related

Update records SQL?

First when I started this project seemed very simple. Two tables, field tbl1_USERMASTERID in Table 1 should be update from field tbl2_USERMASTERID Table 2. After I looked deeply in Table 2, there is no unique ID that I can use as a key to join these two tables. Only way to match the records from Table 1 and Table 2 is based on FIRST_NAME, LAST_NAME AND DOB. So I have to find records in Table 1 where:
tbl1_FIRST_NAME equals tbl2_FIRST_NAME
AND
tbl1_LAST_NAME equals tbl2_LAST_NAME
AND
tbl1_DOB equals tbl2_DOB
and then update USERMASTERID field. I was afraid that this can cause some duplicates and some users will end up with USERMASTERID that does not belong to them. So if I find more than one record based on first,last name and dob those records would not be updated. I would like just to skip and leave them blank. That way I wouldn't populate invalid USERMASTERID. I'm not sure what is the best way to approach this problem, should I use SQL or ColdFusion (my server side language)? Also how to detect more than one matching record?
Here is what I have so far:
UPDATE Table1 AS tbl1
LEFT OUTER JOIN Table2 AS tbl2
ON tbl1.dob = tbl2.dob
AND tbl1.fname = tbl2.fname
AND tbl1.lname = tbl2.lname
SET tbl1.usermasterid = tbl2.usermasterid
WHERE LTRIM(RTRIM(tbl1.usermasterid)) = ''
Here is query where I tried to detect duplicates:
SELECT DISTINCT
tbl1.FName,
tbl1.LName,
tbl1.dob,
COUNT(*) AS count
FROM Table1 AS tbl1
LEFT OUTER JOIN Table2 AS tbl2
ON tbl1.dob = tbl2.dob
AND tbl1.FName = tbl2.first
AND tbl1.LName = tbl2.last
WHERE LTRIM(RTRIM(tbl1.usermasterid)) = ''
AND LTRIM(RTRIM(tbl1.first)) <> ''
AND LTRIM(RTRIM(tbl1.last)) <> ''
AND LTRIM(RTRIM(tbl1.dob)) <> ''
GROUP BY tbl1.FName,tbl1.LName,tbl1.dob
Some data after I tested query above:
First Last DOB Count
John Cook 2008-07-11 2
Kate Witt 2013-06-05 1
Deb Ruis 2016-01-22 1
Mike Bennet 2007-01-15 1
Kristy Cruz 1997-10-20 1
Colin Jones 2011-10-13 1
Kevin Smith 2010-02-24 1
Corey Bruce 2008-04-11 1
Shawn Maiers 2016-08-28 1
Alenn Fitchner 1998-05-17 1
If anyone have idea how I can prevent/skip updating duplicate records or how to improve this query please let me know. Thank you.
You could check for and avoid duplicate matches using with common_table_expression (Transact-SQL)
along with row_number()., like so:
with cte as (
select
t.fname
, t.lname
, t.dob
, t.usermasterid
, NewUserMasterId = t2.usermasterid
, rn = row_number() over (partition by t.fname, t.lname, t.dob order by t2.usermasterid)
from table1 as t
inner join table2 as t2 on t.dob = t2.dob
and t.fname = t2.fname
and t.lname = t2.lname
and ltrim(rtrim(t.usermasterid)) = ''
)
--/* confirm these are the rows you want updated
select *
from cte as t
where t.NewUserMasterId != ''
and not exists (
select 1
from cte as i
where t.dob = i.dob
and t.fname = i.fname
and t.lname = i.lname
and i.rn>1
);
--*/
/* update those where only 1 usermasterid matches this record
update t
set t.usermasterid = t.NewUserMasterId
from cte as t
where t.NewUserMasterId != ''
and not exists (
select 1
from cte as i
where t.dob = i.dob
and t.fname = i.fname
and t.lname = i.lname
and i.rn>1
);
--*/
I use the cte to extract out the sub query for readability. Per the documentation, a common table expression (cte):
Specifies a temporary named result set, known as a common table expression (CTE). This is derived from a simple query and defined within the execution scope of a single SELECT, INSERT, UPDATE, or DELETE statement.
Using row_number() to assign a number for each row, starting at 1 for each partition of t.fname, t.lname, t.dob. Having those numbered allows us to check for the existence of duplicates with the not exists() clause with ... and i.rn>1
You could use a CTE to filter out the duplicates from Table1 before joining:
; with CTE as (select *
, count(ID) over (partition by LastName, FirstName, DoB) as IDs
from Table1)
update a
set a.ID = b.ID
from Table2 a
left join CTE b
on a.FirstName = b.FirstName
and a.LastName = b.LastName
and a.Dob = b.Dob
and b.IDs = 1
This will work provided there are no exact duplicates (same demographics and same ID) in table 1. If there are exact duplicates, they will also be excluded from the join, but you can filter them out before the CTE to avoid this.
Please try below SQL:
UPDATE Table1 AS tbl1
INNER JOIN Table2 AS tbl2
ON tbl1.dob = tbl2.dob
AND tbl1.fname = tbl2.fname
AND tbl1.lname = tbl2.lname
LEFT JOIN Table2 AS tbl3
ON tbl3.dob = tbl2.dob
AND tbl3.fname = tbl2.fname
AND tbl3.lname = tbl2.lname
AND tbl3.usermasterid <> tbl2.usermasterid
SET tbl1.usermasterid = tbl2.usermasterid
WHERE LTRIM(RTRIM(tbl1.usermasterid)) = ''
AND tbl3.usermasterid is null

SQL Union Count to Sum Data

I have a bit of a complex query .... I need to do an update statement on the summation of two union-ed SQL queries (problem is the data in the queries isn't numeric so i'm counting rows instead of summing values) but I then need to sum those rows.
UPDATE #LT_Actuals_TEMP
SET pCount = h.countPerfs
FROM (
select count(distinct c.perf_description) as countPerfs, b.program, b.Prog_id
from #LT_Actuals_TEMP TableP
where a.Performances = 'Y' and a.current_inactive = 0
group by b.Program, b.Prog_id
union
select distinct count(p.perf_code) as countPerfs, x.value, b.Prog_id
from T_PERF p
where x.content_type = 23
group by x.value, b.Prog_id
) h where h.Prog_id = #LT_Actuals_TEMP.program_id
the first query data comes back as such
countPerfs program Prog_id
7 Name 31
and second query comes back as
countPerfs program Prog_id
1 Name 31
what I need pCount to be set to at the end of the day is 8
Expected results
when I do select * from #LT_Actuals_TEMP
I see the value
8 for the Program Name, Id 31
You can solve it by adding another level in the from part where you sum up the data returned from the union.
Your query seems to be missing some source tables (as there are aliases used that don't point to anything) so I guess you're removed some parts, but in general it should look something like this:
UPDATE #LT_Actuals_TEMP
SET pCount = h.sum_of_countperfs
FROM (
select program, prog_id, sum(countPerfs) as sum_of_countperfs
from (
select count(distinct c.perf_description) as countPerfs, b.program, b.Prog_id
from #LT_Actuals_TEMP TableP
where a.Performances = 'Y' and a.current_inactive = 0
group by b.Program, b.Prog_id
union all
select distinct count(p.perf_code) as countPerfs, x.value, b.Prog_id
from T_PERF p
where x.content_type = 23
group by x.value, b.Prog_id
) as sub_q group by program, prog_id
) h where h.Prog_id = #LT_Actuals_TEMP.program_id
Also, you probably want to use union all so that duplicates are not removed.

Select Count Top Inner Join and Where Clause in SQL

This is my Query:
SELECT TOP 3 tablestudentanswer.examid,
tablestudentanswer.studentid,
tablestudentanswer.itemno,
tablestudentanswer.studentanswer,
tablescore.score
FROM tablestudentanswer
INNER JOIN tablescore
ON tablestudentanswer.studentid = tablescore.studentid
AND tablestudentanswer.examid = tablescore.examid
WHERE tablestudentanswer.examid = 1
AND tablestudentanswer.itemno = 1
ORDER BY tablescore.score ASC
It returns this table:
ExamID StudentID ItemNo StudentAnswer Score
1006 1 1 A 25
1005 1 2 B 30
1004 1 3 A 35
What i want to do is it will return 2 if StudentAnswer='A' and 1 if StudentAnswer='B'
Guys there is nothing wrong with my query on top. What i am asking is what should I add in that query.
I have this which in my mind should return 2 but its an error.
Select COUNT(*) From (
Select Top 3 TableStudentAnswer.ExamID, TableStudentAnswer.StudentID, TableStudentAnswer.ItemNo, TableStudentAnswer.StudentAnswer, TableScore.Score
from TableStudentAnswer
Inner join TableScore on TableStudentAnswer.StudentID=TableScore.StudentID and TableStudentAnswer.ExamID=TableScore.ExamID
where TableStudentAnswer.ExamID=1 and TableStudentAnswer.ItemNo=1
Order By TableScore.Score Asc) where TableStudentAnswer.StudentAnswer = 'A'
It should return:
2
Please help me!
Will this do?
SELECT TOP 3 tablestudentanswer.examid,
tablestudentanswer.studentid,
tablestudentanswer.itemno,
tablestudentanswer.studentanswer,
tablescore.score,
case
when tablestudentanswer.studentanswer = 'A' then 2
when tablestudentanswer.studentanswer = 'B' then 1
else NULL
end as [MyColumn]
FROM tablestudentanswer
INNER JOIN tablescore
ON tablestudentanswer.studentid = tablescore.studentid
AND tablestudentanswer.examid = tablescore.examid
WHERE tablestudentanswer.examid = 1
AND tablestudentanswer.itemno = 1
ORDER BY tablescore.score ASC
Your question is a bit unclear. Perhaps you want the amount of answers for each?
count(1) over (partition by tablestudentanswer.studentanswer)
This will give you a column with the amount of all the answers with the given studentanswer to each of the rows in the result set. However, note that this could be quite slow. If you can, you're better off using a normal group by.
Do you mean you would like the query to return the number of answers? If so, using COUNT may help.
SELECT tablestudentanswer.studentid,
tablestudentanswer.studentanswer
COUNT(1) AS NumberOfAnswers
FROM tablestudentanswer
INNER JOIN tablescore
ON tablestudentanswer.studentid = tablescore.studentid
AND tablestudentanswer.examid = tablescore.examid
GROUP BY tablestudentanswer.studentid, tablestudentanswer.studentanswer
Please correct me if I am wrong.
By the way, why does your result table doesn't consist of itemno even though you have it in your SELECT statement?

Sql query to create teams

I need a query to assign teams to a series of users. Data looks like this:
UserId Category Team
1 A null
2 A null
3 B null
4 B null
5 A null
6 B null
8 A null
9 B null
11 B null
Teams should be created by sorting by userid and the first userid becomes the team number and the consecutive A's are part of that team as are the B's that follow. The first A after the Bs starts a new team. There will always be at least one A and one B. So after the update, that data should look like this:
UserId Category Team
1 A 1
2 A 1
3 B 1
4 B 1
5 A 5
6 B 5
8 A 8
9 B 8
11 B 8
EDIT:
Need to add that the user id's will not always increment by 1. I edited the example data to show what I mean. Also, the team ID doesn't strictly have to be the id of the first user, as long as they end up grouped properly. For example, users 1 - 4 could all be on team '1', users 5 and 6 on team '2' and users 8,9 and 11 on team '3'
First you could label each row with an increasing number. Then you can use a left join to find the previous user. If the previous user has category 'B', and the current one category 'A', that means the start of a new team. The team number is then the last UserId that started a new team before the current UserId.
Using SQL Server 2008 syntax:
; with numbered as
(
select row_number() over (order by UserId) rn
, *
from Table1
)
, changes as
(
select cur.UserId
, case
when prev.Category = 'B' and cur.Category = 'A' then cur.UserId
when prev.Category is null then cur.UserId
end as Team
from numbered cur
left join
numbered prev
on cur.rn = prev.rn + 1
)
update t1
set Team = team.Team
from Table1 t1
outer apply
(
select top 1 c.Team
from changes c
where c.UserId <= t1.UserId
and c.Team is not null
order by
c.UserId desc
) as team;
Example at SQL Fiddle.
You can do this with a recursive CTE:
with userCTE as
(
select UserId
, Category
, Team = UserId
from users where UserId = 1
union all
select users.UserId
, users.Category
, Team = case when users.Category = 'A' and userCTE.Category = 'B' then users.UserId else userCTE.Team end
from userCTE
inner join users on users.UserId = userCTE.UserId + 1
)
update users
set Team = userCTE.Team
from users
inner join userCTE on users.UserId = userCTE.UserId
option (maxrecursion 0)
SQL Fiddle demo.
Edit:
You can update the CTE to get this to go:
with userOrder as
(
select *
, userRank = row_number() over (order by userId)
from users
)
, userCTE as
(
select UserId
, Category
, Team = UserId
, userRank
from userOrder where UserId = (select min(UserId) from users)
union all
select users.UserId
, users.Category
, Team = case when users.Category = 'A' and userCTE.Category = 'B' then users.UserId else userCTE.Team end
, users.userRank
from userCTE
inner join userOrder users on users.userRank = userCTE.userRank + 1
)
update users
set Team = userCTE.Team
from users
inner join userCTE on users.UserId = userCTE.UserId
option (maxrecursion 0)
SQL Fiddle demo.
Edit:
For larger datasets you'll need to add the maxrecursion query hint; I've edited the previous queries to show this. From Books Online:
Specifies the maximum number of recursions allowed for this query.
number is a nonnegative integer between 0 and 32767. When 0 is
specified, no limit is applied.
In this case I've set it to 0, i.e. not limit on recursion.
Query Hints.
I actually ended up going with the following. It finished on all 3 million+ rows in a half an hour.
declare #userid int
declare #team int
declare #category char(1)
declare #lastcategory char(1)
set #userid = 1
set #lastcategory='B'
set #team=0
while #userid is not null
begin
select #category = category from users where userid = #userid
if #category = 'A' and #lastcategory = 'B'
begin
set #team = #userid
end
update users set team = #team where userid = #userid
set #lastcategory = #category
select #userid = MIN(userid) from users where userid > #userid
End

Joining two queries horizontally

I wrote two queries below that produce one row of data each.
What is the best way to combine them such that I am LEFT with only a single row of data?
These are coming FROM two DISTINCT databases named : [ASN01] and [dsi_ASN_dsicx]
I have 70 pairs of databases like this but am showing only one for simplicity.
The fact that the three letter acronym ASN is common to both database names is no mistake and if needed can be a part of the solution.
Current Results:
Site, Elligence (header)
ASN, 100.00
Site, GP_Total (header)
ASN, 120.00
Desired results:
Site, GP_Total, Elligence (header)
ASN, 120.00, 100.00
SELECT 'ASN' AS Site ,
CASE SUM(perdblnc)
WHEN NULL THEN 0
ELSE -1 * SUM(PERDBLNC)
END AS GP_Total
FROM [ASN01].[dbo].[GL10110] T1
LEFT OUTER JOIN [ASN01].[dbo].[GL00105] T2 ON [T1].[ACTINDX] = [T2].[ACTINDX]
WHERE YEAR1 = 2012
AND PERIODID IN ( '2' )
AND ACTNUMST IN ( '4200-0000-C', '6940-0000-C', '6945-0000-C',
'6950-0000-C' )
SELECT 'ASN' AS [Site] ,
SUM(pi.amount) AS [Elligence]
FROM [dsi_ASN_dsicx].dbo.charge c
LEFT JOIN [dsi_ASN_dsicx].dbo.paymentitem pi ON c.idcharge = pi.chargeid
LEFT JOIN [dsi_ASN_dsicx].dbo.payment p ON pi.paymentid = p.idpayment
LEFT JOIN [dsi_ASN_dsicx].dbo.paymenttype pt ON p.paymenttypeid = pt.idpaymenttype
WHERE pi.amount != 0
AND pt.paymentmethod NOT IN ( '5', '7' )
AND pt.paymentmethod IS NOT NULL
AND p.sdate >= '20120201'
AND p.sdate <= '20120229'
WIthout going through and changing any of your queries, the easiest way would be to use temp tables using the "WITH" common_table_expression. Table1 and Table2 are temp tables created from your select statements. Therefore, we select table1 and join table2.
Let me know if there are any syntax problems, I don't have anything to test this on presently.
;With Table1 as (SELECT 'ASN' as Site, Case sum(perdblnc)
WHEN NULL THEN 0
ELSE -1*sum(PERDBLNC) END as GP_Total
FROM [ASN01].[dbo].[GL10110] T1
Left Outer Join [ASN01].[dbo].[GL00105] T2
ON [T1]. [ACTINDX]= [T2]. [ACTINDX]
WHERE YEAR1 = 2012
AND PERIODID in ('2')
AND ACTNUMST in ('4200-0000-C', '6940-0000-C', '6945-0000-C', '6950-0000-C'))
, Table2 as (SELECT
'ASN' as [Site],
SUM(pi.amount) as [Elligence]
FROM [dsi_ASN_dsicx].dbo.charge c
LEFT JOIN [dsi_ASN_dsicx].dbo.paymentitem pi on c.idcharge = pi.chargeid
LEFT JOIN [dsi_ASN_dsicx].dbo.payment p on pi.paymentid = p.idpayment
LEFT JOIN [dsi_ASN_dsicx].dbo.paymenttype pt on p.paymenttypeid = pt.idpaymenttype
WHERE pi.amount != 0
AND pt.paymentmethod not in ('5','7')
AND pt.paymentmethod is not null
AND p.sdate >='20120201' and p.sdate <= '20120229')
SELECT * FROM Table1
LEFT JOIN Table2 ON Table1.site = Table2.site
Hope this helps! Marks as answer if it is =)

Resources