Trying to Get SELECT TOP to work with Parameter in ACCESS - sql

This is building on some code I got the other day (thanks to peterm). I am now trying to select the TOP X number of results after calculations on the query. The X can range from 1 to 8 depending on the number of results per player.
This is the code I have but I get a syntax error when I try to run it.
SELECT
PlayerID
, RoundID
, PlayedTo
, (SELECT Count(PlayerID) FROM PlayedToCalcs) AS C
, iif(
C <= 6
, 1
, iif(
C <= 8
, 2
, (
iif(
C <= 10
, 3
, (
iif(
C <= 12
, 4
, (
iif(
C <= 14
, 5
, (
iif(
C <= 16
, 6
, (
iif(
C <= 18
, 7
, (iif(C <= 20, 8, 999))
)
)
)
)
)
)
)
)
)
)
)
) AS X
FROM PlayedToCalcs AS s
WHERE PlayedTo IN (
SELECT TOP (X) PlayedTo
FROM PlayedToCalcs
WHERE PlayerID = s.PlayerID
ORDER BY PlayedTo DESC, RoundID DESC
)
ORDER BY PlayerID, PlayedTo DESC, RoundID DESC;
Here is a link http://sqlfiddle.com/#!3/a726c/4 with a small sample of the data I'm trying to use it on.

The Access db engine does not allow you to use a parameter for SELECT TOP. You must include a literal value in the SQL statement.
For example this query works correctly.
SELECT TOP 2 *
FROM tblFoo
ORDER BY id DESC;
But attempting to substitute a parameter, how_many, triggers error 3141, "The SELECT statement includes a reserved word or an argument name that is misspelled or missing, or the punctuation is incorrect."
SELECT TOP how_many *
FROM tblFoo
ORDER BY id DESC;

The reason being in SQL Server (the simulator you used in SQL Fiddle), you cannot use IIF. Try using CASE.
And there is a limitation of using 7 nested IIF in Access.

Related

How to add rows to a specific number multiple times in the same query

I already asked for help on a part of my problem here.
I used to get 10 rows no matter if there are filled or not. But now I'm facing something else where I need to do it multiple times in the same query result.
WITH NUMBERS AS
(
SELECT 1 rowNumber
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
UNION ALL
SELECT 5
UNION ALL
SELECT 6
UNION ALL
SELECT 7
UNION ALL
SELECT 8
UNION ALL
SELECT 9
UNION ALL
SELECT 10
)
SELECT DISTINCT sp.SLC_ID, c.rowNumber, c.PCE_ID
FROM SELECT_PART sp
LEFT JOIN (
SELECT b.*
FROM NUMBERS
LEFT OUTER JOIN (
SELECT a.*
FROM (
SELECT SELECT_PART.SLC_ID, ROW_NUMBER() OVER (ORDER BY SELECT_PART.SLC_ID) as
rowNumber, SELECT_PART.PCE_ID
FROM SELECT_PART
WHERE SELECT_PART.SLC_ID = (must be the same as sp.SLC_ID and can''t hardcode it)
) a
) b
ON b.rowNumber = NUMBERS.rowNumber
) c ON c.SLC_ID = sp.SLC_ID
ORDER BY sp.SLC_ID, c.rowNumber
It works fine for the first 10 lines, but next SLC_ID only got 1 empty line
I need it to be like that
SLC_ID rowNumer PCE_ID
1 1 0001
1 2 0002
1 3 NULL
1 ... ...
1 10 NULL
2 1 0011
2 2 0012
2 3 0013
2 ... ...
2 10 0020
3 1 0021
3 ... ...
Really need it that way to build a report.
Instead of manually building a query-specific number list where you have to include every possible number you need (1 through 10 in this case), create a numbers table.
DECLARE #UpperBound INT = 1000000;
;WITH cteN(Number) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY s1.[object_id]) - 1
FROM sys.all_columns AS s1
CROSS JOIN sys.all_columns AS s2
)
SELECT [Number] INTO dbo.Numbers
FROM cteN WHERE [Number] <= #UpperBound;
CREATE UNIQUE CLUSTERED INDEX CIX_Number ON dbo.Numbers([Number])
WITH
(
FILLFACTOR = 100, -- in the event server default has been changed
DATA_COMPRESSION = ROW -- if Enterprise & table large enough to matter
);
Source: mssqltips
Alternatively, since you can't add data, use a table that already exists in SQL Server.
WITH NUMBERS AS
(
SELECT DISTINCT Number as rowNumber FROM master..spt_values where type = 'P'
)
SELECT DISTINCT sp.SLC_ID, c.rowNumber, c.PCE_ID
FROM SELECT_PART sp
LEFT JOIN (
SELECT b.*
FROM NUMBERS
LEFT OUTER JOIN (
SELECT a.*
FROM(
SELECT SELECT_PART.SLC_ID, ROW_NUMBER() OVER (ORDER BY SELECT_PART.SLC_ID) as
rowNumber, SELECT_PART.PCE_ID
FROM SELECT_PART
WHERE SELECT_PART.SLC_ID = (must be the same as sp.SLC_ID and can''t hardcode it)
) a
) b
ON b.rowNumber = NUMBERS.rowNumber
) c ON c.SLC_ID = sp.SLC_ID
ORDER BY sp.SLC_ID, c.rowNumber
NOTE: Max value for this solution is 2047

Transpose row to column in SQL Server

I have a table like below:
Type PKG_HA_01_ON PKG_HA_03_ON PKG_HA_04_ON PKG_HA_05_ON PKG_HA_06_ON PKG_HA_09_ON
duration 18.6694 60 15.1951 56.2068 13.6808 13.8404
counter 5 0 5 11 2 0
The first row is the header. Now, I would like to transpose table into this
Machine Duration Counter
PKG_HA_01_ON 18.6694 5
PKG_HA_03_ON 60 0
...
I have tried unpivot but the result is not desired table.
Thanks in advance,
Try this:
create table unpivot_raw(
[Type] nvarchar(255)
, PKG_HA_01_ON float null
, PKG_HA_03_ON float null
, PKG_HA_04_ON float null
, PKG_HA_05_ON float null
, PKG_HA_06_ON float null
, PKG_HA_09_ON float null
)
insert into unpivot_raw
select 'duration', 18.6694, 60, 15.1951, 56.2068, 13.6808, 13.8404
union
select 'counter', 5, 0, 5, 11, 2, 0
select
*
from
(
select
[Type]
, vl
, Machine
from
(
select
[Type]
, PKG_HA_01_ON
, PKG_HA_03_ON
, PKG_HA_04_ON
, PKG_HA_05_ON
, PKG_HA_06_ON
, PKG_HA_09_ON
from unpivot_raw
) p
unpivot
(
vl for Machine in
(
PKG_HA_01_ON
, PKG_HA_03_ON
, PKG_HA_04_ON
, PKG_HA_05_ON
, PKG_HA_06_ON
, PKG_HA_09_ON
)
) unpvt
) base
pivot
(
max(vl) for [Type] in (duration, counter )
) pvt
I recommend using cross apply to unpivot and then aggregation:
select machine,
max(case when type = 'duration' then val end) as duration,
max(case when type = 'counter' then val end) as counter
from t cross apply
(values ('PKG_HA_01_ON', PKG_HA_01_ON),
('PKG_HA_03_ON', PKG_HA_03_ON),
('PKG_HA_04_ON', PKG_HA_04_ON),
('PKG_HA_05_ON', PKG_HA_05_ON),
('PKG_HA_06_ON', PKG_HA_06_ON),
('PKG_HA_09_ON', PKG_HA_09_ON)
) v(machine, val)
group by machine;
I much, much prefer this over pivot/unpivot. Why? APPLY implements a lateral join, which is a powerful construct in SQL (and part of the SQL standard albeit with slightly different syntax). Unpivoting is a nice introduction to this feature.
The pivoting functions are bespoke functions and not part of the standard. They also do not generalize in any way, being designed for a single purpose.

SQL to Mimic Excel

I have an finance issue that we are trying to put into a SQLServer 2005 database (default installation). We are doing quarterly comparison from this year to the same quarter last year i.e. (2013Q1 - 2012Q1) / 20132Q1. Can someone write a query to return an ordered list from 1 - 9 with the quarter over quarter as described above?
In data set
QUART REV
2011Q1 6,175,352
2011Q2 6,591,067
2011Q3 6,219,978
2011Q4 6,189,939
2012Q1 7,178,652
2012Q2 6,731,467
2012Q3 6,949,978
2012Q4 6,679,939
2013Q1 6,242,802
2013Q2 6,421,902
2013Q3 6,667,007
2013Q4 6,575,004
Expected output
QUART COMP
1 0.1625
2 0.0213
3 0.1174
4 0.0792
5 -0.1304
6 -0.0460
7 -0.0407
8 -0.0157
Thanks in advance ;-)
I agree with the above comment, it is much easier if you split quart:
create table t
( yr int not null
, qt int not null
, salary int not null
, primary key (yr,qt) )
insert into t (yr,qt,salary)
values (2011,1,6175352)
, (2011,2,6591067)
, (2011,3,6219978)
, (2011,4,6189939)
, (2012,1,7178652)
, (2012,2,6731467)
, (2012,3,6949978)
, (2012,4,6679939)
, (2013,1,6242802)
, (2013,2,6421902)
, (2013,3,6667007)
, (2013,4,6575004)
select row_number() over (order by yr, qt) as quart, comp
from (
select t1.yr, t1.qt
, (1.0*t1.salary - (select salary
from t t2
where t2.yr = t1.yr - 1
and t2.qt = t1.qt)
) / t1.salary comp
from t t1
where t1.yr >= 2012
)
my numbers deviates from yours, I have not investigate why but it should give you a start.
Lot of formatting in a SQL query, but the exercise was fun. Normally, you should elevate formatting (such as rounding) to the application level, but since you're trying to emulate Excel...
/*sample data*/
DECLARE #T TABLE ( Quart CHAR(6), Rev INT )
INSERT INTO #T
( Quart, Rev )
VALUES ( '2011Q1', 6175352 ),
( '2011Q2', 6591067 ),
( '2011Q3', 6219978 ),
( '2011Q4', 6189939 ),
( '2012Q1', 7178652 ),
( '2012Q2', 6731467 ),
( '2012Q3', 6949978 ),
( '2012Q4', 6679939 ),
( '2013Q1', 6242802 ),
( '2013Q2', 6421902 ),
( '2013Q3', 6667007 ),
( '2013Q4', 6575004 );
/*query begins here
cte is used to parse quart column into years & quarters */
WITH cte
AS ( SELECT Yr = CONVERT(SMALLINT, LEFT(Quart, 4))
, Qt = RIGHT(Quart, 1)
, Rev
FROM #T
)
/*join cte to itself to compare last year same quarter
ROW_NUMBER used to get sequential ordering
CONVERT to numeric and rounding to get formatting
*/
SELECT QUART = ROW_NUMBER() OVER (ORDER BY b.Yr
, b.Qt) ,
COMP = CONVERT(NUMERIC(5,4), ROUND((a.Rev-b.Rev*1.0)/ b.Rev, 4))
FROM cte a
JOIN cte b ON b.Qt = a.Qt
AND b.Yr = a.Yr - 1

Complex 'order by' or maybe sorting issue

I have a table that looks like this
Group Recipe Priority
0 A 400
0 A 200
0 B 500
0 B 100
1 C 300
1 C 300
1 D 600
Importance is "Group" > "Priority" > "Recipe"
Group 0 has to go first.
Within Group 0, Priority 500 has to go first (since it has higher priority), but for the efficiency, all the recipe has to go first.
After sorting,
it should look like this
Group Recipe Priority
0 B 500
0 B 100
0 A 400
0 A 200
1 D 600
1 C 300
1 C 300
I have tried all different ways to do 'order by' but cannot find a correct way.
thank you for the help
The problem is subtle. You want to order not by priority, but by the maximum priority for a given group/recipe combination. That is, you want to keep all the recipes together based on this max priority.
The following does this:
select t.Group, t.Recipe, t.Priority,
max(priority) over (partition by t.group, t.recipe) as maxpriority
from tablename t
order by t.Group asc, 4 desc, t.Recipe, priority desc
In older versions of Oracle, prior to having analytic functions available, we would have returned the specified result set using a query something like this:
SELECT f.group
, f.recipe
, f.priority
FROM foo f
JOIN ( SELECT g.group
, g.recipe
, MAX(g.priority) AS max_priority
FROM foo g
GROUP BY g.group, g.recipe
) m
ON m.group = f.group AND m.recipe = f.recipe
ORDER BY f.group
, m.max_priority DESC
, f.recipe
, f.priority DESC
This approach works in other databases that don't have analytic functions, such as MySQL.
NOTE: The query above is not NULL-safe, in that the JOIN predicates will eliminate rows that have NULL values for the group or recipe columns. It could be made NULL-safe, but it complicates the SQL a bit.
SELECT f.group
, f.recipe
, f.priority
FROM foo f
JOIN ( SELECT g.group
, g.recipe
, MAX(g.priority) AS max_priority
FROM foo g
GROUP BY g.group, g.recipe
) m
ON (m.group = f.group OR COALESCE(m.group,f.group) IS NULL)
AND (m.recipe = f.recipe OR COALESCE(m.recipe,f.recipe) IS NULL)
ORDER BY f.group
, m.max_priority DESC
, f.recipe
, f.priority DESC
An equivalent result can also be obtained using a correlated subquery in the SELECT list, except that this result set contains an extra "max_priority" column in the result set.
SELECT f.group
, f.recipe
, f.priority
, (SELECT MAX(g.priority)
FROM foo g
WHERE (g.group = f.group OR COALESCE(g.group,f.group) IS NULL)
AND (g.recipe = f.recipe OR COALESCE(g.recipe,f.recipe) IS NULL)
) AS max_priority
FROM foo f
ORDER BY f.group
, 4 DESC
, f.recipe
, f.priority DESC
(I haven't tested whether that correlated subquery could be removed from the SELECT list and entirely moved to the ORDER BY clause. If that worked, we'd eliminate returning the extra column, but that query would look really, really odd.) The other option (to omit that extra column) is to wrap this query (as an inline view) in another query.
SELECT e.group
, e.recipe
, e.priority
FROM (
SELECT f.group
, f.recipe
, f.priority
, (SELECT MAX(g.priority)
FROM foo g
WHERE (g.group = f.group OR COALESCE(g.group,f.group) IS NULL)
AND (g.recipe = f.recipe OR COALESCE(g.recipe,f.recipe) IS NULL)
) AS max_priority
FROM foo f
) e
ORDER BY e.group
, e.max_priority DESC
, e.recipe
, e.priority DESC

coverage percentage using a complex sql query...?

Ok, i've been trying to solve this for about 2 hours now... Please advise:
Tables:
PROFILE [id (int), name (varchar), ...]
SKILL [id (int), id_profile (int), id_app (int), lvl (int), ...]
APP [id (int), ...]
The lvl can basically go from 0 to 3.
I'm trying to get this particular stat:
"What is the percentage of apps that is covered by at least two people having a skill of 2 or higher?"
Thanks a lot
SELECT AVG(covered)
FROM (
SELECT CASE WHEN COUNT(*) >= 2 THEN 1 ELSE 0 END AS covered
FROM app a
LEFT JOIN skill s ON (s.id_app = a.id AND s.lvl >= 2)
GROUP BY a.id
)
More efficient way for MySQL:
SELECT AVG
(
IFNULL
(
(
SELECT 1
FROM skill s
WHERE s.id_app = a.id
AND s.lvl >= 2
LIMIT 1, 1
), 0
)
)
FROM app a
This will stop counting as soon as it finds the second skilled person for each app.
Efficient if you have a few app's but lots of person's.
Untested
select convert(float,count(*)) / (select count(*) from app) as percentage
from (
select count(*) as number
from skill
where lvl >= 2
group by id_app ) t
where t.number >= 2
The logic is: percentage = 100 * ( number of apps of interest ) / ( total number of apps )
select 'percentage' =
-- 100 times
( cast( 100 as float ) *
-- number of apps of interest
( select count(id_app)
from ( select id_app, count(*) as skilled_count
from skill
where lvl >= 2
group by id_app
having count(*) >= 2 ) app_counts )
-- divided by total number of apps
/ ( select count(*) from app )
The convert to float is needed so sql doesn't just do integer arithmetic.
SELECT SUM( CASE lvl WHEN 3 THEN 1 WHEN 2 THEN 1 ELSE 0 END ) / SUM(1) FROM SKILL
If your database has an if/then function instead of CASE, use that. For example, in MySQL:
SELECT SUM( IF( lvl >= 2, 1, 0 ) ) / SUM(1) FROM SKILL
I'm not sure if this is any better or worse than tvanfosson's answer, but here it is anyway:
SELECT convert(float, count(*)) / (Select COUNT(id) FROM APP) AS percentage
FROM APP INNER JOIN SKILL ON APP.id = SKILL.id
WHERE (
SELECT COUNT(id)
FROM SKILL AS Skill2 WHERE Skill2.id_app = APP.id and lvl >= 2
) >= 2