LIFO Stack that Pushes every Pop in SQL - sql

Given an example table such as:
CREATE TABLE TESTING_Order(
Order INT,
Name VARCHAR(5)
)
INSERT INTO TESTING_Order
VALUES
(0, 'Zero'),
(1, 'One'),
(2, 'Two'),
(3, 'Three'),
(4, 'Four'),
(5, 'Five'),
(6, 'Six'),
(7, 'Seven')
I'd like to know how to implement a sort of 'Stack' to show, for instance, the turn order in a game where the first player moves 'left' each turn. I can accomplish this using two update statements:
UPDATE TESTING_Order
SET Order = Order + 1
UPDATE TESTING_Order
SET Order = (SELECT Min(Order) -1 FROM TESTING_Order)
WHERE Order = (SELECT MAX(Order) FROM TESTING_Order)
I was wondering if there is a cleaner/more proper way of doing this, and especially if that other way can be done using a single UPDATE statement.
In other words, I think what I'm after is a better implementation of a LIFO Stack that performs a push on every pop--excuse me for possibly butchering any terminology.

Just so that there is an answer here:
To cycle through the list I've decided to use:
UPDATE TESTING_Order
SET nOrder =
Case
WHEN (nOrder - 1) < 0 THEN (SELECT Count(*) FROM TESTING_Order) - 1
WHEN (nOrder - 1) >= 0 THEN (nOrder - 1) % (SELECT MAX(nOrder) + 1 FROM TESTING_Order)
END
This allows me to move the list in the correct way (which I realize my answer probably did not) and has the benefit of doing what I requested in just one statement. Thanks for pointing me in the right direction, Wiseguy.

Related

How to query the same set of columns with different set of values on the same query efficiently

I'm using SQL SERVER 2014 and I have this query which needs to be rebuilt to be more efficient in what it is trying to accomplish.
As an example, I created this schema and added data to it so we could replicate the problem. You can try it at rextester (http://rextester.com/AIYG36293)
create table Dogs
(
Name nvarchar(20),
Owner_ID int,
Shelter_ID int
);
insert into Dogs values
('alpha', 1, 1),
('beta', 2, 1),
('charlie', 3, 1),
('beta', 1, 2),
('alpha', 2, 2),
('charlie', 3, 2),
('charlie', 1, 3),
('beta', 2, 3),
('alpha', 3, 3);
I want to find out which Shelter has these set of owner and dog name combinations and it must be exact. This is the query I'm using right now (this is more or less what query Entity Framework generated but with some slight changes to make it simpler):
SELECT DISTINCT
Shelter_ID
FROM Dogs AS [Extent1]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM [Dogs] AS [Extent2]
WHERE [Extent1].[Shelter_ID] = [Extent2].[Shelter_ID] AND [Extent2].[Name] = 'charlie' AND [Extent2].[Owner_ID] = 1
)) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Dogs] AS [Extent3]
WHERE [Extent1].[Shelter_ID] = [Extent3].[Shelter_ID] AND [Extent3].[Name] = 'beta' AND [Extent3].[Owner_ID] = 2
)) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Dogs] AS [Extent4]
WHERE [Extent1].[Shelter_ID] = [Extent4].[Shelter_ID] AND [Extent4].[Name] = 'alpha' AND [Extent4].[Owner_ID] = 3
))
This query is able to get what I need but I want to know if there is any simpler way of querying it. Because in my actual use case, I have more than just 3 combinations to worry about, it could get up to some crazy combinations like 1000 or more. So just imagine having 1000 subqueries in there so, well, yeah you get the point. When I try querying with that many I get an error saying:
The query processor ran out of internal resources and could not
produce a query plan. This is a rare event and only expected for
extremely complex queries or queries that reference a very large
number of tables or partitions.
NOTE
One solution I tried was using a Pivot to flatten the data and although the query becomes simpler since it would then be just a simple WHERE clause with a number of AND statements but when at some point I get to a higher number number of combinations then I exceed the limit for the allowable max row size and get this error when creating my temporary table to store the flatten data:
Cannot create a row of size 10514 which is greater than the allowable
maximum row size of 8060.
I appreciate any help or thoughts on this matter.
Thanks!
Count them.
WITH dogSet AS (
SELECT *
FROM (
VALUES ('charlie',1),('beta',2),('alpha',3)
) ts(Name,Owner_ID)
)
SELECT Shelter_ID
FROM Dogs AS [Extent1]
JOIN dogSet ts ON ts.Name= [Extent1].name and ts.Owner_ID = [Extent1].Owner_ID
GROUP BY Shelter_ID
HAVING count(*) = (SELECT count(*) n FROM dogSet)

Applying multiple conditions in SQL?

I haven't been able to find a similar question for an answer I'm looking for. What is the best way to apply multiple conditions to my query to exclude certain information. Case or Boolean?
Example code:
SELECT test,
testTypeID,
visitType,
submitted
FROM vReport
WHERE (vReport.submitted = 0 OR vReport.submitted IS NULL)
AND vReport.test IN ('Test 1','Test 2','Test 3')
How do I best code for it to return all the tests 1, 2, and 3 while excluding rows for certain visit types (i.e. exclude row ONLY if it is Test 3 AND it is visit Week 26 AND a certain testTypeID)?
<>Not sure what your column names and datatypes are for visitWeek (assuming this is an INT) and testTypeID and what values you want to filter by but here is the logic for it:
SELECT test,
testTypeID,
visitType,
submitted
FROM vReport
WHERE (vReport.submitted = 0 OR vReport.submitted IS NULL)
AND vReport.test IN ('Test 1','Test 2','Test 3')
AND (vReport.test NOT IN ('Test 3') AND vReport.testTypeID NOT IN (some value) AND vReport.visitWeek <> 26)
If you can define your exclusions homogeneously, you can store them in another table. Something like:
ExcludedTest
excludedTestId
test
visitType
testTypeId
and your query can be done like this:
SELECT test,
testTypeID,
visitType,
submitted
FROM vReport VR
WHERE (vReport.submitted = 0 OR vReport.submitted IS NULL)
AND vReport.test IN ('Test 1','Test 2','Test 3')
AND NOT EXISTS ( SELECT 1 FROM ExcludedTest ET
WHERE ET.testTypeID = VR.testTypeID
AND ET.visitType = VR.visitType
AND ET.test = VR.test)
Also, you should have a better performance if you exclude that OR. One way to do this is to keep submitted as NOT NULL with DEFAULT(0) => vReport.submitted = 0 condition is enough.

How can I sort version numbers such as ‘A’, ‘B’, ‘AA’, ‘AB’ and ‘AAA’?

In reference to this question: How do I grab only the latest Invoice Number
I accepted an answer that uses the MAX function but Robert McKee pointed out that will result in sorted values such as:
‘A’
‘AA’
‘AAA’
‘AB’
‘B’
When what I need is:
‘A’
‘B’
‘AA’
‘AB’
‘AAA’
I am trying to find a way to find the latest Version of an Invoice. The accepted answer from the referenced question will work up to a point. And it did satisfy my question... But now a new problem deserves its own question and not for me to go back and modify my original question. So…
The only thing I have to work with is the Invoice Number itself.
The Invoice number has a format of #####XXX, where ##### is the actual Invoice Number and the XXX is the version number. XXX can be anywhere from ‘A’ to ‘ZZZ’.
Here is my attempt to find a plausible work around (a sql test case):
DECLARE #TempTable TABLE (MyNumber int, MyString varchar(15));
INSERT #TempTable
VALUES (100, 'A'), (100, 'AAZ'), (100, 'B'), (100, 'AZ'), (100, 'C'), (100, 'Z'), (100, 'AA'), (100, 'AB');
SELECT TOP 1
RTRIM(CAST(MyNumber AS NVARCHAR(15)) + MyString) AS InvoiceNumber
FROM #TempTable
ORDER BY RIGHT(LEFT(MyString + SPACE(2), 3), 1) DESC, RIGHT(LEFT(MyString + SPACE(2), 2), 1) DESC, LEFT(MyString, 1) DESC;
Would anyone care to provide a better answer or point me in the right direction to clean mine up?
Thanks in advance,
Try something like this:
ORDER BY LEN(myValue),myValue
this will order the 1-character, then the 2-character, etc...
Not sure if this meets your definition of "better" or "cleaned up":
ORDER BY LEFT(MyString,1),SUBSTRING(MyString,2,1),SUBSTRING(MyString,3,1)

How to get the root parentID of a product or menu Item [duplicate]

This question already has answers here:
SQL - how to get the top parent of a given value in a self referencing table
(3 answers)
Closed 8 years ago.
I need to get the root PageID of a child item which actually in my case is Menu item.
i have a table structure as below
[PageId], [PageName], [PagePath], [PageInheritance]
What i want is a sql query that will get me the PageID if users select level 2 or level 3 menu item. so that i can always highlight the parent menu irrespective of its level
for example if PageID = 6 then it it should get me Root PageID as 2.
I also tried to set of SQL Fiddle for this page but it fails for some reason.
CREATE TABLE PageMenu
([PageId] int, [PageName] varchar(5), [PagePath] varchar(50), [PageInheritance] int)
;
INSERT INTO PageMenu
([PageId], [PageName], [PagePath], [PageInheritance])
VALUES
(1, 'Home', '/en/', 0),
(2, 'Menu1', '/en/Menu1/', 0),
(3, 'Child1', '/en/Menu1/Child1/', 2),
(4, 'Child1', '/en/Menu1/Child2/', 2),
(5, 'GrandChild1', '/en/Menu1/Child1/GrandChild1/', 4),
(6, 'GrandChild2', '/en/Menu1/Child1/GrandChild2/', 5)
;
My Solution : http://rextester.com/IXEKB9577
This is a bit challenging because you do not want "1", but "2". So, you want the second level, which makes this a bit different from most such problems. Here is one way to get the top level:
select pm.*, t.PageId
from PageMenu pm cross apply
(select top 1 pm2.PageId
from PageMenu pm2
where pm.PageName like pm2.PageName + '%'
order by len(pm2.PageName)
) t;
However, this returns "1", and not "2". You can do this with simple filtering
select pm.*, t.PageId
from PageMenu pm cross apply
(select top 1 pm2.PageId
from PageMenu pm2
where pm.PagePath like pm2.PagePath + '%' and
pm2.PageName <> 'Home'
order by len(pm.PageName)
) t;
Here is a SQL Fiddle

How to check between range of values of same column

This is the table I have:
Now, I want to check if the input is between 95-91 or 80-90 or 70-79...and so on.
How can I do that ?
Here we join the table to itself to get the min and max values for each grade.
select
g1.Courseid,
g1.GradeValue MinGradeValue,
isnull(min(g2.GradeValue)-1,100) MaxGradeValue,
g1.Description
from YourTable g1
left join YourTable g2
ON g2.CourseId = g1.CourseId
and g2.GradeValue > g1.GradeValue
group by
g1.Courseid,
g1.GradeValue,
g1.Description
You can join this as a CTE or something to a Student's grade with Student.Grade between MinGradeValue and MaxGradeValue. Let me know if I can help you further.
First off, stop thinking in inclusive upper-bound ranges; read this post about BETWEEN (which is an inclusive range) - this applies to anything that is conceptually not an integral count (ie, pretty much everything). What happens when somebody gets a grade of 79.5?
Fortunately, your table is perfectly setup for constructing a bounding-range table (which can be done as a CTE here, or as a materialized view if strictly necessary). I tend to prefer OLAP functions for this sort of work (and 2012 has a nice one for this):
SELECT courseId, description,
gradeValue as minimumValue,
LEAD(gradeValue) OVER(PARTITION BY courseId ORDER BY gradeValue) as nextGradeMinimumValue
FROM Grade
... Which you can then query against similar to this:
SELECT StudentGrade.studentId, StudentGrade.courseId, StudentGrade.grade,
Grade.description
FROM (VALUES(1, 1, 38),
(2, 1, 99),
(3, 2, 74.5),
(4, 2, 120)) StudentGrade(studentId, courseId, grade)
JOIN (SELECT courseId, description,
gradeValue as minimumValue,
LEAD(gradeValue) OVER(PARTITION BY courseId ORDER BY gradeValue) as nextGradeMinimumValue
FROM Grade) Grade
ON Grade.courseId = StudentGrade.courseId
AND Grade.minimumValue >= StudentGrade.grade
AND (Grade.nextGradeMinimumValue IS NULL OR Grade.nextGradeMinimumValue > StudentGrade.grade)
(ordinarily I'd have an SQL Fiddle example, but I can't access it at the moment, so this is untested).
This should work for all (positive) grade ranges, including an unlimited amount of "extra credit" (any score higher than the top boundary is assigned that description).