Data result table containing sub tables in TSQL? - tsql

I am working on a products page that I have to query all data at once and one of the columns should contain multiple values. In this particular case it's a list of products containing multiple pictures. Unfortunately I cannot squeeze all pictures in the same column to slice it later because the data will be processed in a XML.CONFIG page that handle data with for-each. That's what I have.
In short I would like if there is a way of select data in a way where each record has a "sublist".
Example...
PRODUCT TABLE:
Id ProdId Desc
1 9003 Shirt
2 9004 Trouser
PICTURE TABLE:
Id PicId ProdId
1 A 9003
2 B 9003
3 C 9004
4 D 9004
5 E 9003
RESULT OF THE QUERY:
ProdId Desc PicId
9003 Shirt
A
B
E
9004 Trouser
C
D
I know that it may sound crazy but I remember that around 20 years ago I made something similar using SQL Server Express and Crystal Reports in a VB program for church administration. We had a report listing congregational members and grouping their donations below each member. I am SURE that everything I did was to run a SQL query that did this job but it was so many time ago and I can't remember how it was done.
Any idea?
NOTE ADDED:
I just remembered that by the time I used the command "SHAPE". I found a sample in T-SQL documentation but don't know why it's not working. My code:
SHAPE {SELECT ProdId,Desc FROM product} AS PRO
APPEND ({SELECT PicId FROM pics} AS PIC
RELATE PRO.ProdId TO PIC.ProdId)
This is weird. I am doing exactly as the example but it says there is a sintax error near to }.
The T-SQL documentation:
https://learn.microsoft.com/mt-mt/sql/ado/guide/data/shape-append-clause?view=sql-server-2017

That's a simple left join on prodid.
SELECT pr.prodid,
pr.desc,
pi.picid
FROM product pr
LEFT JOIN picture pi
ON pi.prodid = pr.prodid
ORDER BY pr.prodid,
pi.picid;
which will get you
prodid
desc
left
9003
Shirt
A
9003
Shirt
B
9003
Shirt
E
9004
Trouser
C
9004
Trouser
D
The rest is presentation layer logic as #Larnu commented. You'd basically loop through the result and print the header line for a product each time you encounter one that wasn't already processed. In either case print the picture line.

If you are using SQL 2017 you can use the STRING_AGG function. If not then I would use the FOR XML clause such as:
CREATE TABLE #PRODUCT(Id int, ProdId int, [Desc] varchar(50))
INSERT INTO #PRODUCT VALUES
(1,9003,'Shirt'),
(2,9004,'Trouser')
CREATE TABLE #PICTURE(Id int, PicId varchar(4), ProdId int)
INSERT INTO #PICTURE VALUES
(1,'A', 9003),
(2,'B', 9003),
(3,'C', 9004),
(4,'D', 9004),
(5,'E', 9003)
FOR SQL 2017 and above:
select p.id,p.ProdId, STRING_AGG(pt.PicID, CHAR(13)) PicID
from #PRODUCT p
INNER JOIN #PICTURE PT on p.ProdId = pt.ProdId
GROUP BY p.Id,p.ProdId
FOR SQL 2016 and below:
SELECT p.id,p.ProdId,
STUFF(
(SELECT CHAR(13) + pt.PicID
FROM #PICTURE pt
WHERE p.ProdId = pt.ProdId
FOR XML PATH (''))
, 1, 1, '') PicID
FROM #PRODUCT p
GROUP BY p.Id,p.ProdId

Related

How to make postgres (cursor?) start at particular row

I have created the following query:
select t.id, t.row_id, t.content, t.location, t.retweet_count, t.favorite_count, t.happened_at,
a.id, a.screen_name, a.name, a.description, a.followers_count, a.friends_count, a.statuses_count,
c.id, c.code, c.name,
t.parent_id
from tweets t
join accounts a on a.id = t.author_id
left outer join countries c on c.id = t.country_id
where t.row_id > %s
-- order by t.row_id
limit 100
Where %s is a number that starts at 0 and is incremented by 100 after each such query is conducted. I want to fetch all records from the database using this method, where I just increase the %s in the where condition. I found this approach on https://ivopereira.net/efficient-pagination-dont-use-offset-limit. I also included a column in my table which is corresponding to row number (I named it row_id). Now the problem is when I run this query the first time, it returns rows which have an row_id of 3 million. I would like the cursor (not sure if my terminology is correct) to start from rows with row_id 1 through 100 and so on. The table contains 7 million rows. Am I missing something obvious with which I could achieve my goal?

How can I list those ID's what's not in the where clause?

I've got 2 tables Books and Copy's.
I counted those copy's where the time between 2 dates and grouped by bookID, what's between 2 dates and I've got the following result:
BookID Copy Quantity
111111 1
222222 888
333333 666
SELECT BookID, Count(CopyID)
from Copy
INNER join Book on Book.BookID = Copy.BookdID
where Copy.Time BETWEEN '2017.12.11' AND '2017.12.15'
GROUP BY BOOKID
Here is my problem: I want to count all that copy what's got the same BookID in my query.
For example: I've got 1 result for BookID 111111,but if there is 8 copy I want to somehow include to my result.
Ben, following query displays count of all copies for books where there is at least one copy between given two dates
Please check if it outputs what is required
select
b.BookID, Count(c.CopyID)
from Book as b
left outer join Copy as c
on b.BookID = c.BookID
where b.BookID in (
select
distinct c.BookID
from Copy as c
where c.Time BETWEEN '2017.12.11' AND '2017.12.15'
)
group by
b.BookID
I don't think you even need to book table
select c.ID, count(*)
from copy c
where exists ( select 1
from copy
where copy.ID = c.ID
and copy.Time BETWEEN '2017.12.11' AND '2017.12.15'
)
group by c.ID

SSRS 2005 column chart: show series label missing when data count is zero

I have a pretty simple chart with a likely common issue. I've searched for several hours on the interweb but only get so far in finding a similar situation.
the basics of what I'm pulling contains a created_by, person_id and risk score
the risk score can be:
1 VERY LOW
2 LOW
3 MODERATE STABLE
4 MODERATE AT RISK
5 HIGH
6 VERY HIGH
I want to get a headcount of persons at each risk score and display a risk count even if there is a count of 0 for that risk score but SSRS 2005 likes to suppress zero counts.
I've tried this in the point labels
=IIF(IsNothing(count(Fields!person_id.value)),0,count(Fields!person_id.value))
Ex: I'm missing values for "1 LOW" as the creator does not have any "1 LOW" they've assigned risk scores for.
*here's a screenshot of what I get but I'd like to have a column even for a count when it still doesn't exist in the returned results.
#Nathan
Example scenario:
select professor.name, grades.score, student.person_id
from student
inner join grades on student.person_id = grades.person_id
inner join professor on student.professor_id = professor.professor_id
where
student.professor_id = #professor
Not all students are necessarily in the grades table.
I have a =Count(Fields!person_id.Value) for my data points & series is grouped on =Fields!score.Value
If there were a bunch of A,B,D grades but no C & F's how would I show labels for potentially non-existent counts
In your example, the problem is that no results are returned for grades that are not linked to any students. To solve this ideally there would be a table in your source system which listed all the possible values of "score" (e.g. A - F) and you would join this into your query such that at least one row was returned for each possible value.
If such a table doesn't exist and the possible score values are known and static, then you could manually create a list of them in your query. In the example below I create a subquery that returns a combination of all professors and all possible scores (A - F) and then LEFT join this to the grades and students tables (left join means that the professor/score rows will be returned even if no students have those scores in the "grades" table).
SELECT
professor.name
, professorgrades.score
, student.person_id
FROM
(
SELECT professor_id, score
FROM professor
CROSS JOIN
(
SELECT 'A' AS score
UNION
SELECT 'B'
UNION
SELECT 'C'
UNION
SELECT 'D'
UNION
SELECT 'E'
UNION
SELECT 'F'
) availablegrades
) professorgrades
INNER JOIN professor ON professorgrades.professor_id = professor.professor_id
LEFT JOIN grades ON professorgrades.score = grades.score
LEFT JOIN student ON grades.person_id = student.person_id AND
professorgrades.professor_id = student.professor_id
WHERE professorgrades.professor_id = 1
See a live example of how this works here: SQLFIDDLE
SELECT RS.RiskScoreId, RS.Description, SUM(DT.RiskCount) AS RiskCount
FROM (
SELECT RiskScoreId, 1 AS RiskCount
FROM People
UNION ALL
SELECT RiskScoreId, 0 AS RiskCount
FROM RiskScores
) DT
INNER JOIN RiskScores RS ON RS.RiskScoreId = DT.RiskScoreId
GROUP BY RS.RiskScoreId, RS.Description
ORDER BY RS.RiskScoreId

SQL Server 2008 R2 Grouping

Here is the script I have.
select jt.exempt_status as ExemptStatus, COUNT(*) as ExemptCount
from employee as e
join job_title as jt
on e.job_title = jt.job_title
group by jt.exempt_status
When executed it displays
ExemptStatus ExemptCount
-----------------------------
E 2
N 5
What I need it to display without changing the values in the jt.exempt_status field is.
ExemptStatus ExemptCount
-----------------------------
Exempt 2
Non-Exempt 5
You can use a CASE expression to format your values when selecting them:
select CASE jt.exempt_status
WHEN 'E' THEN 'Exempt'
WHEN 'N' THEN 'Non-Exempt'
END
as ExemptStatus, COUNT(*) as ExemptCount
from employee as e
join job_title as jt
on e.job_title = jt.job_title
group by jt.exempt_status

AdventureWorks SQL conflicting results issue

I'm working with the AdventureWorks example DB - we're running SQL Server 2008R2, so I assume that's the edition of AdventureWorks (I have read-only access). I'm trying to get a list of sales managers so that I can then determine a couple employee/manager relationships.
I'm getting two sets of three differently named people, with the same job title, with their CurrentFlag set to 1 (active) with slightly different queries. I do notice that one result group has the same contactID and employeeID, but I'm not sure what this may indicate.
So the question is: Why am I getting completely different results with these two queires? I would think I'd get six results for each - the queries are matching employee table Titles.
SQL Query 1:
select
c.FirstName,
c.LastName,
c.ContactID,
e.EmployeeID,
e.Title,
c.Title,
e.CurrentFlag
from Person.Contact c
inner join HumanResources.Employee e
on c.ContactID = e.ContactID
where
e.Title like '%Sales Manager%'
SQL Query 2:
SELECT
e.EmployeeID,
(c.FirstName + ' ' + c.LastName) as 'First Name and Last Name',
e.Title
FROM HumanResources.Employee e
INNER JOIN Person.Contact c
ON e.EmployeeID = c.ContactID
Where
e.Title LIKE '%Manager%'
AND
e.Title LIKE '%Sales%'
ORDER BY e.EmployeeID;
UPDATE: These are my results:
SQL Query 1:
------- ------- ---- --- ---------------------------- ---- --
Stephen Jiang 1011 268 North American Sales Manager NULL 1
Amy Alberts 1013 284 European Sales Manager NULL 1
Syed Abbas 1012 288 Pacific Sales Manager Mr. 1
SQL Query 2:
--- --- ----------- ---------------------------- --- --
268 268 Gary Drury North American Sales Manager Mr. 1
284 284 John Emory European Sales Manager Mr. 1
288 288 Julie Estes Pacific Sales Manager Ms. 1
The only diffrents i can see is this:
where
e.Title like '%Sales Manager%'
And this:
Where
e.Title LIKE '%Manager%'
AND
e.Title LIKE '%Sales%'
The first query says that bring me all titles that has '%Sales Manager%' you can have for ex this output:
Account Sales Manager
some Sales Manager
Sales Manager something else
The second question says bring me all the titles that has '%Manager%' and '%Sales%' so you can for ex have:
Sales Account Manager
some Sales some Manager some
Sales Manager some else thing
Manager Sales
And this join can not be corrent
INNER JOIN Person.Contact c
ON e.EmployeeID = c.ContactID
Don't you mean:
INNER JOIN Person.Contact c
ON e.ContactID= c.ContactID
The first query will match the rows where substring "Sales Manager" is present. But second one can match rows like "Managers of Sales Dep" as well. I mean the second doesn't care about positions of the words in the srting.
I believe that the results of first query is a subset of the results of second one.
UPDATE
You use different columns in JOIN clause, so it's normal that you got different results.