Equation in SQL query - sql

I am trying to identify duplicate data within our database.
If somebodys first seat is 1, and their last seat is 3, it means they should have x3 num_seats
So i want to run a query where first_seat - last_seat +1 = num_seats. Any pointers on what is wrong with my query?
select acct_id, event_id, event_name, section_name, row_name, first_seat, last_seat, num_seat
from dba.v_event
where first_seat - last_seat +1 != num_seat

Your equation is backward. It should be last_seat - first_seat + 1.
In your example, first_seat = 1 and last_seat = 3.
first_seat - last_seat + 1 = 1 - 3 + 1 = -1
last_seat - first-seat + 1 = 3 - 1 + 1 = 3
If you want to allow the seats to be listed in either order, you can use ABS() to get the absolute value of the difference:
ABS(last_seat - first_seat) + 1

Related

Performing sum per row based on conditions SQL

I wanted to know how I can perform a sum per row based on conditions for the columns using SQL (I'm new to SQL).
For example, I have this table:
ID Col_1 Col_2 Col_3 ...
1 L L L ...
2 L Q Q ...
3 L L Q ...
4 Q Q L ...
The result that I'm looking for is:
ID count_L count_Q
1 3 0
2 1 2
3 2 1
4 1 2
I'm not sure on how I should approach this. Doing this using Count function if my table was transposed would be easier (I think) but performing the query in the way my data is organized is tricky for me. I think I need nested SQL statements and join them using UNION but not sure how to do it.
I wasn't able to find similar questions/solutions elsewhere. Would appreciate some help!
You can use iif() and +:
select id,
(iif(col_1 = "L" , 1, 0) + iif(col_2 = "L" , 1, 0) + iif(col_3 = "L" , 1, 0) ) as count_l,
(iif(col_1 = "Q" , 1, 0) + iif(col_2 = "Q" , 1, 0) + iif(col_3 = "Q" , 1, 0) ) as count_q
from t;

Get max number from table add one and check with specific convention

I have to produce artikel number based on some convention, and this convention is as below
The number of digits
{1 or 2 or 3}.{4 or 5}.{n}
example products numbers:
7.1001.1
1.1453.1
3.5436.1
12.7839.1
12.3232.1
13.7676.1
3.34565.1
12.56433.1
247.23413.1
The first part is based on producent, and every producent has its own number. Let's say Rebook - 12, Nike - 256 and Umbro - 3.
I have to pass this number and check in table if there are some rows containing it e.g i pass 12 then i should get everything which starts from 12.
and now there should be three cases what to do:
1st CASE: no rows at the table:
then retrieve 1001
2nd case: if there are rows
so for sure there is already at least one:
12.1001.1
and more if they are let's say:
12.1002.1
12.1003.1
...
12.4345.1
so should be retreived next one so: 4346
and if there are already 5-digits for this product so let's say:
12.1002.1
12.1003.1
...
12.9999.1
so should be retreived next one so: 10001
3rd case: in fact same as 2nd but if it rached 9999 for second part:
12.1001.1
...
12.9999.1
then returned should be: 10001
or
12.1002.1
12.1003.1
...
12.9999.1
12.10001.1
12.10002.1
so should be retreived next one so: 10003
Hope you know what i mean
I already have started something. This code is taking producent number - looking for all rows starting with it and then just simply adding 1 to the second part unfortunetly i am not sure how should i change it according to those 3 cases.
select
parsename(max(nummer), 3) + '.' -- 3
+ ltrim(max(cast(parsename(nummer, 2) as int) +1)) -- 5436 -> 5437
+ '.1'
from tbArtikel
where Nummer LIKE '3.%'
Counting on your help. If something unclear let me know.
Additional question:
Using cmd As New SqlCommand("SELECT CASE WHEN r.number Is NULL THEN 1001
WHEN r.number = 9999 THEN 10001
Else r.number + 1 End number
FROM (VALUES(#producentNumber)) AS a(art) -- this will search this number within inner query And make case..
LEFT JOIN(
-- Get producent (in Like) number And max number Of it (without Like it Get all producent numbers And their max number out Of all
SELECT PARSENAME(Nummer, 3) art,
MAX(CAST(PARSENAME(Nummer, 2) AS INT)) number
FROM tbArtikel WHERE Nummer Like '#producentNumber' + '[.]%'
GROUP BY PARSENAME(Nummer, 3)
) r
On r.art = a.art", con)
cmd.CommandType = CommandType.Text
cmd.Parameters.AddWithValue("#producentNumber", producentNumber)
A fairly straight forward way is to (ab)use PARSENAME to split the string to be able to extract the current maximum. An outer query can then just implement the rules for the value being missing/9999/other.
The value (12 here) is inserted in a table value constructor to be able to detect a missing value using a LEFT JOIN.
SELECT CASE WHEN r.number IS NULL THEN 1001
WHEN r.number = 9999 THEN 10001
ELSE r.number + 1 END number
FROM ( VALUES(12) ) AS a(category)
LEFT JOIN (
SELECT PARSENAME(prodno, 3) category,
MAX(CAST(PARSENAME(prodno, 2) AS INT)) number
FROM products
GROUP BY PARSENAME(prodno, 3)
) r
ON r.category = a.category;
An SQLfiddle to test with.
As a further optimization, you could add a WHERE prodno LIKE '12[.]%' in the inner query to not parse through un-necessary rows.
I don't fully understand what you're asking for. I am unsure about the examples...but if i was doing it I'd try to break the field into 3 fields first and then do something with them.
sqlfiddle
SELECT nummer,LEFT(nummer,first-1) as field1,
RIGHT(LEFT(nummer,second-1),second-first-1) as field2,
RIGHT(nummer,LEN(nummer)-second) as field3
FROM
(SELECT nummer,
CHARINDEX('.',nummer) as first,
CHARINDEX('.',nummer,CHARINDEX('.',nummer)+1)as second
from tbArtikel)T
Hopefully with the 3 fields broken up, it's much easier to apply logics to them now.
update:
Okay i reread your question and i sort of know what you're trying to get at..
if user search for a value that doesn't exist for example 8.
Then you want 1001 returned
if they search for anything else that has results then return the max+1
unless it's 9999 then return 10001.
If this is correct then check this sqlfiddle2
DECLARE #search varchar(20)
SET #search = '8'
SELECT field1,max(nextvalue) as nextvalue FROM
(SELECT field1,
MAX(CASE (field2)
WHEN 9999 THEN 10001
ELSE field2+1
END) as nextvalue
FROM
(SELECT nummer,
CAST(LEFT(nummer,first-1) as INTEGER) as field1,
CAST(RIGHT(LEFT(nummer,second-1),second-first-1) as INTEGER) as field2,
CAST(RIGHT(nummer,LEN(nummer)-second) as INTEGER) as field3
FROM
(SELECT nummer,
CHARINDEX('.',nummer) as first,
CHARINDEX('.',nummer,CHARINDEX('.',nummer)+1)as second
FROM tbArtikel
)T
)T2
GROUP BY field1
UNION
SELECT CAST (#search as INTEGER)as field1 ,1001
)T3
WHERE field1 = #search
GROUP BY field1
Just change the #search variable to see it's results
I think there might be a cleaner way to do this but it's not coming to me right now :(
If you really can't add 2 new fields (is't probably the simplest and fastest solution), and probably can't add functional index, you must extract 2nd part number and get max of this, increment, then concatenate with your condition 1st part number and '.1' at the end:
SELECT :par1 || '.' || (Max(To_Number(SubStr(nummer, dot1 + 1, dot2 - dot1 -1 ))) + 1) || '.1' NEW_number
--SELECT SubStr(nummer, 1, dot1 - 1) N1st, SubStr(nummer, dot1 + 1, dot2 - dot1 -1 ) N2nd, SubStr(nummer, dot2 + 1) N1th
FROM (
SELECT nummer, InStr(nummer, '.') dot1, InStr(nummer, '.', 1, 2) dot2
FROM tbArtikel
WHERE nummer LIKE :par1 || '.%')
;
--GROUP BY SubStr(nummer, 1, dot1 – 1)
it was for oracle sql, i don't have sql-serwer to test, but probably this is simplest answer:
select #par1 + '.' + (select max(cast(SUBSTRING(nummer, CHARINDEX( '.', nummer, 1 ) +1, CHARINDEX( '.', nummer, CHARINDEX( '.', nummer, 1 ) +1 ) - CHARINDEX( '.', nummer, 1 ) -1) as int)) + 1 from tbArtikel where nummer LIKE #par1 || '.%') + '.1'
if parsename(nummer, 2) is you defined function to get 2nd number then:
select #parm + '.' + (max(cast(parsename(nummer, 2) as int)) + 1) + '.1'
from tbArtikel
where Nummer LIKE #parm + '.%'

How do I get Average IF statement in SQL?

I have table in MS Access with columns Year, Period (values just 1 and 2), Costs_Per_Capita and CALCULATED_Period_Avg_Costs, PK is ID.
I need to calculate CALCULATED_Period_Avg_Costs. It should return average of Costs_Per_Capita for given period from Period column. In Excel I do this with SUMIF/COUNTIF which returns Average IF.
Do you have some advice how to write a code in SQL to do that?
Results should look like:
Costs_Per_Capita Period CALCULATED_Period_Avg_Costs
15,505 1 15976.27582
16,368 1 15976.27582
16,037 1 15976.27582
15,995 1 15976.27582
15,000 2 16000
17,000 2 16000
I used statement:
SELECT
Costs_Per_Capita, Period
IFF (Period = 1,
(Select AVG(Costs_Per_Capita) From Costs Where Period = 1),
(Select AVG(Costs_Per_Capita) From Costs Where Period = 2)
AS result
FROM Costs;
Still gets "syntax error (missing operator) in a query expression ..."
I think I know what you're asking for and I believe this is what you're looking for -
Select
Costs_Per_Capita,
Related_Period_ID,
(Select
Case when Related_Period_ID = 1 then
(Select
AVG(Costs_Per_Capita)
From Costs_Per_Capita_Table
Where Related_Period_ID = 1)
else
(Select
AVG(Costs_Per_Capita)
From Costs_Per_Capita_Table
Where Related_Period_ID = 2)
END
)
From
Costs_Per_Capita_Table
Changing CASE to IFF
Select
IFF (Related_Period_ID = 1,
(Select
AVG(Costs_Per_Capita)
From Costs_Per_Capita_Table
Where Related_Period_ID = 1),
(Select
AVG(Costs_Per_Capita)
From Costs_Per_Capita_Table
Where Related_Period_ID = 2)
I did this with:
SELECT Costs.Costs_Per_Capita, Costs.Period,
IIF(Costs.Period = 1,
(Select AVG(Costs_Per_Capita) From Costs Where Period = 1),
(Select AVG(Costs_Per_Capita) From Costs Where Period = 2))
AS result
FROM Costs;
other way, better if more periods apper is using subquery:
SELECT Costs.Period, Costs.Costs_Per_Capita, tmpQry.CALCULATED_Period_Avg_Costs
FROM Costs INNER JOIN (SELECT Costs.Period, Avg(Costs.Costs_Per_Capita)
AS CALCULATED_Period_Avg_Costs FROM Costs
GROUP BY Costs.Period) AS tmpQry ON Costs.Period = tmpQry.Period;
maybe someone will use it later on...
Select CostsPerCapital, Period, avg(CostPerCapita) over (partition by Period)
From Table
Where....
Order by ...

SQL - Pulling data from the same table with two different "where" statements

I have a table with the following columns...
TestName - StepNumber - Data_1
I'm trying to write a query that can look for Data_1 results and average them for one day. The TestNames are unique tests we're running, and StepNumbers are the individual steps inside of the test. Normally, I would use something like
select Data_1 from table
where TestName in(1,2,3,4)
and StepNumber in(1)
to return all of the Data_1 results I need. However, sometimes the data I need is located in different steps across the table. Test 1 might have the required data in Step 2, Test 2 in step 10, etc...and in the end, I need an average of the Data_1 results for all of the similar StepNumber results. I'm not sure how I can capture this data in a single query. There's a separate part of the query where I'm breaking it down by geography, and doing it individually would take a long time.
I'd be looking for something like...
select avg(Data_1) from table
where TestName = 1 and StepNumber = 2
and TestName = 2 and StepNumber = 10
and TestName = 3 and StepNumber = 5
If I can clarify, please let me know. Thank you!
select avg(Data_1)
from table
where (TestName = 1 and StepNumber = 2)
or (TestName = 2 and StepNumber = 10)
or (TestName = 3 and StepNumber = 5)
If I have understood correctly, you have three (or more) sets of data in your table:
TestName = 1 and StepNumber = 2 - cardinality N1
Testname = 2 and StepNumber = 10 - cardinality N2
TestName = 3 and StepNumber = 5 - cardinality N3
If you want the average for all three separately, you needs must select three columns. AVG in this case cannot help you as it would run the average on a cardinality of N4, this being the intersection of the three groups. So you have to do this by hand. I do not know how this would perform vs. three separate queries:
SELECT
AVG(Data_1) AS OverallAverage,
SUM(IF((TestName = 1 AND StepNumber = 2), Data_1, 0))
/SUM(IF((TestName = 1 AND StepNumber = 2), 1, 0)) AS AvgGroup1,
SUM(IF((TestName = 2 AND StepNumber = 10), Data_1, 0))
/SUM(IF((TestName = 2 AND StepNumber = 10), 1, 0)) AS AvgGroup2,
SUM(IF((TestName = 3 AND StepNumber = 5), Data_1, 0))
/SUM(IF((TestName = 3 AND StepNumber = 5), 1, 0)) AS AvgGroup3
FROM table
WHERE (
( TestName = 1 AND StepNumber = 2)
OR
( TestName = 2 AND StepNumber = 10)
OR
( TestName = 3 AND StepNumber = 5)
);
This kind of query can be assembled from components, i.e. programmatically depending on the groups.
This is a SQLFiddle to show the results.

SQL - field concatenation, based on variable

I have a need to build a string from Last Name, First Name, Middle Initial according to the following rules:
If the Last Name is unique, just
return the Last Name
If the Last
Name isn't unique, but the first
letter of the First Name is unique,
return Last Name + first letter of
First Name
If the Last Name and
first letter of the First Name are
not unique, return the Last Name +
first letter of First Name + Middle
Initial.
For example, the table might be:
MDC MDLast MDFirst MDInit
3 Jones Fred A
21 Smith Sam D
32 Brown Tom E
42 Brown Ted A
55 Smith Al D
The query should return:
MDC MDFormattedName
3 Jones
21 Smith S
32 Brown TE
42 Brown TA
55 Smith A
I've written up a query that almost works, but it is using several nested queries, and will still need several more to (possibly) make a workable solution, and is so inefficient. I'm sure there is a 'proper' way to implement this (for SQL Server 2005, BTW).
This is what I've got so far. It doesn't work, due to the aggregations I lose the IDs can can't do the final join to get ID/Name pairs.
select
CASE
WHEN CountLastFirst > 1 THEN
CASE WHEN MDInit IS NOT NULL THEN MDLastFirst + LEFT(MDInit,1) ELSE MDLastFirst END
WHEN CountLastFirst = 1 AND CountLast > 1 THEN MDLastFirst
ELSE MDLast
END as MDName
FROM
(
select x.MDLast, CountLast, MDLastFirst, CountLastFirst FROM
(
select MDLast,Count(MDLast) as CountLast FROM
MDList
GROUP BY MDLast) as x
INNER JOIN
(select MDLast, MDLastFirst,Count(MDLastFirst) as CountLastFirst FROM
(
select MDLast,
MDLast + ' ' + LEFT(MDFirst,1) as MDLastFirst
From MDList
) as a
GROUP BY MDLastFirst, MDLast) as y ON x.MDLast = y.MDLast
) as z
Assuming a table name of MDCTable, this should work:
SELECT MDCTable.MDC,
CASE MDCCount.NameCount
WHEN 1
THEN MDCTable.MDLast
ELSE
CASE MDFormat1Count
WHEN 1
THEN MDFormat1.MDFormat1Name
ELSE MDCTable.MDLast + ' ' + upper(left(MDCTable.MDFirst, 1)) +
MDCTable.MDInit
END
END AS MDFormattedName
FROM MDCTable
INNER JOIN
(
SELECT COUNT(MDLast) as NameCount, MDLast
FROM MDCTable
GROUP BY MDLast
) MDCCount ON MDCCount.MDLast = MDCTable.MDLast
INNER JOIN (
SELECT COUNT(MDLast + left(MDFirst, 1)) as MDFormat1Count, MDLast + ' ' +
left(MDFirst, 1) AS MDFormat1Name
FROM MDCTable
GROUP BY MDLast + ' ' + left(MDFirst, 1)
) MDFormat1 ON MDCTable.MDLast + ' ' + left(MDCTable.MDFirst, 1) =
MDFormat1.MDFormat1Name
ORDER BY MDCTable.MDC
Have you considered performing this operation in your application instead of directly in an SQL statement? Unless you have a good reason to do this directly in SQL, this is almost always the preferable approach for situations like this.