Random Order by in SQL Server - sql-server

I got the data using this query:
SELECT PT.ID AS ProductTypeId,
PT.ProductType,
PTPS.ID AS AssetId,
PTPS.Ordinal AS AssetOrdinal,
CASE
WHEN PTPS.LinkType = 2
THEN
--DS.ServerRootURL + 'Videos/ProductTypes/' +
--PTPSMaterialImage.LinkURL
(
SELECT TOP 1 LinkURL
FROM ProductTypesPblmSolutions
WHERE ProductTypeId = PT.ID
AND LinkType = 1
ORDER BY ID DESC
)
ELSE PTPS.LinkURL
END AS MaterialImage,
ATL.LinkType
FROM ProductTypes PT
INNER JOIN ProductTypesToApps PTA ON PT.ID = PTA.ProductTypeId
INNER JOIN ProductTypesPblmSolutions PTPS ON PTPS.ProductTypeId = PT.ID
INNER JOIN DealerSettings DS ON DS.DealerId = 23
INNER JOIN AttachmentLinkTypes ATL ON ATL.ID = PTPS.LinkType
WHERE PTA.AppId = 3
AND (PT.ID = 202 OR 202 IS NULL)
AND (ISNULL(PTPS.IsDeleted, 0) = 0)
ORDER BY PTPS.Ordinal
Let's talk about the materialimage column. I have to get two different images for this producttype but for now I am getting the same product.
There are lot of linkurls exist for this product. Please refer this query below and its data.
SELECT ProductTypeId,
linkurl,
linktype
FROM ProductTypesPblmSolutions
WHERE ProductTypeId = 202
AND linktype = 1
The same thing I used it as subquery in my Main query
How do I get different images in this column.
I tried with TOP 1 ... ORDER BY NEWID() but it does NOT work.

Just to be clear, the subquery should look like:
(SELECT TOP 1 ptps2.LinkURL
FROM ProductTypesPblmSolutions ptps2
WHERE ptps2.ProductTypeId = PT.Id AND
ptps2.LinkType = 1
ORDER BY NEWID()
)
Note the use of qualified column names and the NEWID() in the ORDER BY.
I recommend always using qualified column names. This is especially true in correlated subqueries, where errors are easy to make and very difficult to find (although that might not be the problem in this particular case).
If you see no effect from this, then I would assume that the else component of the case is being executed, rather than the subquery.

SELECT TOP(1) t.LinkURL
FROM dbo.ProductTypesPblmSolutions t
WHERE t.ProductTypeId = PT.ID
AND t.LinkType = 1
ORDER BY NEWID(), PT.ID -- additional calculation
Example #1:
SELECT TOP(10) s.number, (
SELECT TOP(1) s1.number
FROM [master].dbo.spt_values s1
WHERE s1.[type] = s.[type]
ORDER BY NEWID()
)
FROM [master].dbo.spt_values s
WHERE s.[type] = 'P'
Output:
----------- -----------
0 844
1 844
2 844
3 844
4 844
5 844
6 844
7 844
8 844
9 844
Example #2:
SELECT TOP(10) s.number, (
SELECT TOP(1) s1.number
FROM [master].dbo.spt_values s1
WHERE s1.[type] = s.[type]
ORDER BY NEWID(), s.[type] -- <<<< additional calculation
)
FROM [master].dbo.spt_values s
WHERE s.[type] = 'P'
Output:
----------- -----------
0 428
1 801
2 550
3 1619
4 178
5 17
6 1702
7 683
8 352
9 1844

Related

T-SQL select rows where [col] = MIN([col])

I have a data set produced from a UNION query that aggregates data from 2 sources.
I want to select that data based on whether or not data was found in only of those sources,or both.
The data relevant parts of the set looks like this, there are a number of other columns:
row
preference
group
position
1
1
111
1
2
1
111
2
3
1
111
3
4
1
135
1
5
1
135
2
6
1
135
3
7
2
111
1
8
2
135
1
The [preference] column combined with the [group] column is what I'm trying to filter on, I want to return all the rows that have the same [preference] as the MIN([preference]) for each [group]
The desired output given the data above would be rows 1 -> 6
The [preference] column indicates the original source of the data in the UNION query so a legitimate data set could look like:
row
preference
group
position
1
1
111
1
2
1
111
2
3
1
111
3
4
2
111
1
5
2
135
1
In which case the desired output would be rows 1,2,3, & 5
What I can't work out is how to do (not real code):
SELECT * WHERE [preference] = MIN([preference]) PARTITION BY [group]
One way to do this is using RANK:
SELECT row
, preference
, [group]
, position
FROM (
SELECT row
, preference
, [group]
, position
, RANK() OVER (PARTITION BY [group] ORDER BY preference) AS seq
FROM t) t2
WHERE seq = 1
Demo here
Should by doable via simple inner join:
SELECT t1.*
FROM t AS t1
INNER JOIN (SELECT [group], MIN(preference) AS preference
FROM t
GROUP BY [group]
) t2 ON t1.[group] = t2.[group]
AND t1.preference = t2.preference

SQLite: Return NULL fields in multiple LEFT JOIN Table

I have a cumbersome query I'm building in a certain way as I'll be calling it from the C-API substituting certain values. I'm having an issue where I'm expecting NULL fields to be populating the final table.
To populate my query, I generate a date column using a recursive table expression and joining another table twice.
An example set from my table's data:
SELECT * FROM myTable;
id foreignId date value
---------- ---------- ---------- ----------
1 1 2019-12-01 100
2 1 2019-12-02 101
3 1 2019-12-03 102
4 1 2019-12-04 103
5 1 2019-12-07 104
6 2 2019-12-01 200
7 2 2019-12-02 201
8 2 2019-12-03 202
9 2 2019-12-07 203
The query I'm using:
WITH RECURSIVE dates(date) AS (
VALUES('2019-12-01')
UNION ALL
SELECT date(date, '+1 day')
FROM dates
WHERE date < '2019-12-07'
)
SELECT a.date, b.myTable, c.myTable
FROM dates a
LEFT JOIN myTable b ON a.date = b.date
LEFT JOIN myTable c ON a.date = c.date
WHERE b.foreignId = 1 AND c.foreignId = 2;
Returns the table:
date myTable myTable
---------- ---------- ----------
2019-12-01 100 200
2019-12-02 101 201
2019-12-03 102 202
2019-12-07 104 203
What I am trying to achieve:
date myTable myTable
---------- ---------- ----------
2019-12-01 100 200
2019-12-02 101 201
2019-12-03 102 202
2019-12-04 103
2019-12-05
2019-12-06
2019-12-07 104 203
I've tried using IFNULL in the SELECT statement like:
...
SELECT a.date, IFNULL(b.myTable, 0) b.myTable, IFNULL(c.myTable, 0) c.myTable
...
Which returns:
Error: near ".": syntax error
I'm not certain what the syntax error is, and haven't got that part working to test the result.
I've also tried using CROSS JOIN in place of LEFT JOIN and various combinations, but they return the same table as the LEFT JOIN. Can anyone give me some guidance, particularly anything in the documentation I may have missed?
Move the condition on the left joined tables from the where clause to the on part of the join. Otherwise, rows where the left join comes up empty are filtered out by the where clause (since the conditions are not be fulfilled): this actually turns your left join to an inner join.
WITH RECURSIVE dates(date) AS ( ...)
SELECT a.date, b.myTable, c.myTable
FROM dates a
LEFT JOIN myTable b ON a.date = b.date AND b.foreignId = 1
LEFT JOIN myTable c ON a.date = c.date AND c.foreignId = 2;

Distinct values with occurrences count

I want to count the number of times a vendor title occurs in a resultset, but if I use COUNT combined with GROUP BY, I only get either a 0 or 1 in the resultset
e.g. my results now look like this:
id vendortitle cnt
184 Hotel 1
198 A3 1
199 Las Vegas 1
200 Hotel-Restaurant 1
1252 Hansen 1
1253 Sunrise 1
1255 NULL 0
1256 Winsel 1
1257 Olde North 1
1258 A Castle 1
1259 A Castle 1
1262 Restaurant Rich 1
1263 NULL 0
1264 NULL 0
1265 NULL 0
1266 NULL 0
1269 My venue 1
1270 My venue 1
1271 My venue 1
1272 My venue 1
But I want this (I don't really actually need the NULL values):
id vendortitle cnt
184 Hotel 1
198 A3 1
199 Las Vegas 1
200 Hotel-Restaurant 1
1252 Hansen 1
1253 Sunrise 1
1255 NULL 5
1256 Winsel 1
1257 Olde North 1
1258 A Castle 2
1262 Restaurant Rich 1
1269 My venue 4
My SQL statement:
SELECT DISTINCT(vendortitle),id,COUNT(vendortitle) as cnt FROM (select ROW_NUMBER() OVER (ORDER BY vendortitle DESC) as RowNum,
id,vendortitle
FROM
(
SELECT
uf.id,coalesce(l.title, a.title) as vendortitle
FROM userfavorites uf
INNER JOIN vendor_photos vp ON uf.objectid = vp.id
LEFT JOIN homes l on vp.objecttype= 1 and vp.objectid = l.id
LEFT JOIN hotels a on vp.objecttype= 2 and vp.objectid = a.id
) as info
) as allinfo
WHERE RowNum > 0 AND RowNum <= 50
GROUP BY vendortitle,RowNum, id
There are a lot of things in your query that you don't need. The derived table isn't really needed, the DISTINCT and ROW_NUMBER either. This should do the work:
SELECT MIN(uf.id) id,
COALESCE(l.title, a.title) as vendortitle,
COUNT(*) as cnt
FROM userfavorites uf
INNER JOIN vendor_photos vp
ON uf.objectid = vp.id
LEFT JOIN homes l
ON vp.objecttype= 1
AND vp.objectid = l.id
LEFT JOIN hotels a
ON vp.objecttype = 2
AND vp.objectid = a.id
GROUP BY COALESCE(l.title, a.title);

Performance issue with CTE SQL Server query

We have a table with a parent child relationship, that represents a deep tree structure.
We are using a view with a CTE to query the data but the performance is poor (see code and execution plan below).
Is there any way we can improve the performance?
WITH cte (ParentJobTypeId, Id) AS
(
SELECT
Id, Id
FROM
dbo.JobTypes
UNION ALL
SELECT
e.Id, cte.Id
FROM
cte
INNER JOIN
dbo.JobTypes AS e ON e.ParentJobTypeId = cte.ParentJobTypeId
)
SELECT
ISNULL(Id, 0) AS ParentJobTypeId,
ISNULL(ParentJobTypeId, 0) AS Id
FROM
cte
A quick example of using the range keys. As I mentioned before, hierarchies were 127K points and some sections where 15 levels deep
The cte Builds, let's assume the hier results will be will be stored in a table (indexed as well)
Declare #Table table(ID int,ParentID int,[Status] varchar(50))
Insert #Table values
(1,101,'Pending'),
(2,101,'Complete'),
(3,101,'Complete'),
(4,102,'Complete'),
(101,null,null),
(102,null,null)
;With cteOH (ID,ParentID,Lvl,Seq)
as (
Select ID,ParentID,Lvl=1,cast(Format(ID,'000000') + '/' as varchar(500)) from #Table where ParentID is null
Union All
Select h.ID,h.ParentID,cteOH.Lvl+1,Seq=cast(cteOH.Seq + Format(h.ID,'000000') + '/' as varchar(500)) From #Table h INNER JOIN cteOH ON h.ParentID = cteOH.ID
),
cteR1 as (Select ID,Seq,R1=Row_Number() over (Order by Seq) From cteOH),
cteR2 as (Select A.ID,R2 = max(B.R1) From cteOH A Join cteR1 B on (B.Seq Like A.Seq+'%') Group By A.ID)
Select B.R1
,C.R2
,A.Lvl
,A.ID
,A.ParentID
Into #TempHier
From cteOH A
Join cteR1 B on (A.ID=B.ID)
Join cteR2 C on (A.ID=C.ID)
Select * from #TempHier
Select H.R1
,H.R2
,H.Lvl
,H.ID
,H.ParentID
,Total = count(*)
,Complete = sum(case when D.Status = 'Complete' then 1 else 0 end)
,Pending = sum(case when D.Status = 'Pending' then 1 else 0 end)
,PctCmpl = format(sum(case when D.Status = 'Complete' then 1.0 else 0.0 end)/count(*),'##0.00%')
From #TempHier H
Join (Select _R1=B.R1,A.* From #Table A Join #TempHier B on A.ID=B.ID) D on D._R1 between H.R1 and H.R2
Group By H.R1
,H.R2
,H.Lvl
,H.ID
,H.ParentID
Order By 1
Returns the hier in a #Temp table for now. Notice the R1 and R2, I call these the range keys. Data (without recursion) can be selected and aggregated via these keys
R1 R2 Lvl ID ParentID
1 4 1 101 NULL
2 2 2 1 101
3 3 2 2 101
4 4 2 3 101
5 6 1 102 NULL
6 6 2 4 102
VERY SIMPLE EXAMPLE: Illustrates the rolling the data up the hier.
R1 R2 Lvl ID ParentID Total Complete Pending PctCmpl
1 4 1 101 NULL 4 2 1 50.00%
2 2 2 1 101 1 0 1 0.00%
3 3 2 2 101 1 1 0 100.00%
4 4 2 3 101 1 1 0 100.00%
5 6 1 102 NULL 2 1 0 50.00%
6 6 2 4 102 1 1 0 100.00%
The real beauty of the the range keys, is if you know an ID, you know where it exists (all descendants and ancestors).

SQL Server 2005 - Update column where DATEDIFF between two dates is minimum

I have two tables, defined as following:
PTable:
[StartDate], [EndDate], [Type], PValue
.................................................
2011-07-01 2011-07-07 001 5
2011-07-08 2011-07-14 001 10
2011-07-01 2011-07-07 002 15
2011-07-08 2011-07-14 002 20
TTable:
[Date], [Type], [TValue]
..................................
2011-07-01 001 11
2011-07-02 001 4
2011-07-03 001 0
2011-07-08 002 12
2011-07-09 002 12
2011-07-10 002 0
I want to update Tvalue column in TTable with the PValue in PTable, where [Date] in TTable is between [StartDate] and [EndDate] in PTable and DATEDIFF(DAY,TTable.[Date],PTable.[EndDate]) is minimum, AND PTable.Type = TTable.Type
The final TTable should look like this:
[Date], [Type], [TValue]
..................................
2011-07-01 001 11
2011-07-02 001 4
2011-07-03 001 5 --updated
2011-07-08 002 12
2011-07-09 002 12
2011-07-10 002 20 --updated
What I have tried is this:
UPDATE [TTable]
SET
TValue = T1.PValue
FROM TTable
INNER JOIN PTable T1 ON
[Date] BETWEEN T1.StartDate AND T1.EndDate
AND DATEDIFF(DAY,[Date],T1.EndDate) =
(SELECT MIN( DATEDIFF(DAY,TTable.[Date],T.EndDate) )
FROM PTable T WHERE TTable.[Date] BETWEEN T.StartDate AND T.EndDate
)
AND
T1.[Type] = TTable.[Type]
It gives me this error :
"Multiple columns are specified in an aggregated expression containing an outer reference. If an expression being aggregated contains an outer reference, then that outer reference must be the only column referenced in the expression."
Later edit:
Considering TTable AS T and PTable AS P, the condition for update are:
1. T.Type = P.Type
2. T.Date BETWEEN P.StartDate AND P.EndDate
3. DATEDIFF(DAY,T.Date,P.EndDate) = minimum value of all DATEDIFFs WHERE P.Type = T.Type AND T.Date BETWEEN P.StartDate AND P.EndDate
Later Edit 2:
Sorry, because I typed wrong the last row in PTable (2011-08-10 instead 2011-07-14), the final result was wrong.
I also managed to update in a simpler way, which I obviously should have tried from the start:
UPDATE TTABLE
SET
TValue = T1.PValue
FROM TTable
INNER JOIN PTABLE T1 ON
[Date] = (SELECT TOP(1) MAX(Date) FROM [TTABLE] WHERE [Date] BETWEEN T1.StartDate AND T1.EndDate)
AND
T1.Type = [TTABLE].Type
Sorry about this.
So you said something about "DATEDIFF(DAY,TTable.[Date],PTable.[EndDate]) is minimum" which confused me. Itt would seem like if there a weekly entry per Type, then for a particular Date, Type combination it would ever only match one. You might give this a try:
UPDATE TTABLE
SET TValue = T1.PValue
FROM TTable
INNER JOIN PTABLE T1 ON T1.Type = [TTABLE].Type -- find row in PTable that the Date falls between
and [Date] BETWEEN T1.StartDate AND T1.EndDate)
where
TValue = ( select MIN(TValue) -- finds the lowest TValue, 0 in example
from TTable))
...updated...
So it appears I read the problem incorrectly the first time. I had thought we update the TTable entries that have the lowest TValue. Not sure how I got that impression. Still seems like there needs to be a check for if it is 0?
UPDATE TTable
SET TValue = T1.PValue
FROM TTable
INNER JOIN PTable T1 ON T1.Type = TTable.Type
and T1.EndDate = (
SELECT top 1 EndDate
FROM PTable
WHERE Type=TTable.Type
ORDER BY abs(DATEDIFF(day,TTable.Date,PTable.EndDate)) desc)
WHERE
TValue = 0 -- only updating entries that aren't set, have a 0
This only works if there is one is one row in PTable with an EndDate of 7/7 or whatever for a given type. If there are two entries for Type 001 with an end date of 7/7, then it will join to two entries. Also if there is two entries that are equal distant from the date in question, so an EndDate of 7/7 and one of 7/13 are both 3 days from 7/10. If the EndDates are all 7 days apart (weekly) you should be ok.

Resources