Adding additional criteria to a right join - sql

This is my first post to this forum and I am very new to SQL so please bear with me.
I am attempting to modify some existing script to make a small change to a report to make it slightly more fit for purpose (the original was put together by a developer).
This report is looking to see whether two specific files have been read into a database for three entities each month or whether they are missing.
The output looks likes the below:
File A
YYYY:MM:DD A MISSING
B MISSING
C MISSING
YYYY:MM:DD A Present
B MISSING
C Present
The script is such that only the current year files are looked for with the exception of the files for the December of the previous year, however I want to also display the October and November results for the previous year.
Below is the relevant part of the script:
select distinct(k.filedate) as filedate, k.fid, case when r.fundid is null then 0 else 1 end as present
from XXXX database
right join
(
select convert(date,convert(varchar, year(#ReportDate) - 1) + '-12-01') as filedate, g.fid
from (
select 'XXXXFDGBP10' as fid
union
select 'XXXXUSD10' as fid
union
select 'XXXXUSD10' as fid
union
select 'XXXXA10' as fid
union
select 'XXXXB10' as fid
union
select 'XXXXGBPMGMT10' as fid
union
select 'XXXXMGMTSH10' as fid
) g
union
select convert(date,convert(varchar, year(#ReportDate)) + '-' + convert(varchar, h.m) + '-01') as filedate, s.fid
from (
select 'XXXXFDGBP10' as fid
union
select 'XXXXUSD10' as fid
union
select 'XXXXUSD10' as fid
union
select 'XXXXA10' as fid
union
select 'XXXXB10' as fid
union
select 'XXXXGBPMGMT10' as fid
union
select 'XXXXMGMTSH10' as fid
) s,
(
select 1 as m
union
select 2 as m
union
select 3 as m
union
select 4 as m
union
select 5 as m
union
select 6 as m
union
select 7 as m
union
select 8 as m
union
select 9 as m
union
select 10 as m
union
select 11 as m
union
select 12 as m
) h
) k
on r.fundid = k.fid and r.filedate = k.filedate
where
k.filedate >= convert(date,convert(varchar, year(#ReportDate) - 1) + '-12-01')
and k.filedate <= #ReportDate
So I want to add '11-01' and '10-01' to the 2016 return of the report. Does anyone know how I can do this?
Thanks in advance. Please let me know if this isn't clear or if anyone has any questions.

I'd rewrite your query as
;WITH months AS
(
SELECT * FROM (VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12)) AS f("month")
),
years AS
(
SELECT * FROM (VALUES (year(#ReportDate)), (year(#ReportDate)-1)) AS f("year")
),
fids AS
(
SELECT * FROM (VALUES ('XXXXFDGBP10'), ('XXXXUSD10'), ('XXXXUSD10'), ('XXXXA10'), ('XXXXB10'), ('XXXXGBPMGMT10'), ('XXXXMGMTSH10')) AS f(fid)
),
k AS
(
SELECT
filedate = DATEADD(month, [month]-1, DATEADD(year, [year]-1900, 0)),
fid
FROM fids
CROSS JOIN years
CROSS JOIN months
)
SELECT
k.filedate,
k.fid,
present = case when r.fundid is null then 0 else 1 end
FROM XXXX r
RIGHT JOIN k ON r.fundid = k.fid and r.filedate = k.filedate
where
k.filedate >= convert(date,convert(varchar, year(#ReportDate) - 1) + '-10-01')
and k.filedate <= #ReportDate

Related

Snowflake subquery to simulate COUNTIF

I have the following query why I"m attempting to simulate a COUNTIF across two tables:
WITH Range__A1_A4 (val) AS (
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 8 UNION ALL
SELECT 10
), NFL (SEASON, TEAM, WINS) AS (
SELECT 2010, 'KC', 10 UNION ALL
SELECT 2011, 'GB', 11
)
SELECT
(SELECT SUM(CASE WHEN NFL.WINS>R.val THEN 1 ELSE 0 END) FROM Range__A1_A4 R) ct,
*
FROM NFL;
From this I get the following error:
Unsupported subquery type cannot be evaluated
What's wrong with this query and what would be the proper way to fix it?
I would write it like this:
WITH Range__A1_A4 (val) AS (
SELECT * FROM VALUES
(1),
(2),
(8),
(10)
), NFL (SEASON, TEAM, WINS) AS (
SELECT * FROM VALUES
(2010, 'KC', 10),
(2011, 'GB', 11)
)
SELECT
n.*,
SUM(iff( n.wins > r.val, 1, 0)) as ct_a,
count_if(n.wins > r.val) as ct_b
FROM NFL AS n
JOIN Range__A1_A4 AS r
group by 1,2,3;
SEASON
TEAM
WINS
CT
CT_B
2,010
KC
10
3
3
2,011
GB
11
4
4

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

How to return N records in a SELECT statement without a table

I'm creating a NACHA file and if the number of records in the file is not a multiple of 10, we need to insert enough "dummy" records filled with nines (replicate('9',94)) to hit that next tens place.
I know that I could write a loop or perhaps fill a temp table with 10 records full of nines and select the top N. But those options feel clunky.
I was trying to think of a single select statement that could do it for me. Any ideas?
select nacha_rows
from NACHA_TABLE
union all
select replicate('9',94) --do this 0 to 9 times
The formula (10-COUNT(*)%10)%10 tells you how many rows to add, so you can just select that many dummy rows from an existing dummy table.
SELECT nacha_rows
FROM NACHA_TABLE
UNION ALL
SELECT TOP (SELECT (10-COUNT(*)%10)%10 FROM NACHA_TABLE) REPLICATE('9',94)
FROM master.dbo.spt_values
This should work. Created a temp table with 9 rows of the dummy data. Then used modulo to determine how many extra rows should be returned. Then return the full dataset. If you wanted to make it pretty you could take the modulo piece out and calculate it one time in a variable.
;WITH dummydata (num, nines)
AS (SELECT 1 AS num, Replicate('9', 94)
UNION ALL
SELECT num + 1, Replicate('9', 94)
FROM dummydata
WHERE num < 9)
SELECT *
FROM nacha_table
UNION ALL
SELECT nines
FROM dummydata
WHERE num >= CASE
WHEN (SELECT Count(1) % 10 FROM nacha_table) = 0 THEN 10
ELSE (SELECT Count(1) % 10 FROM nacha_table)
END
One idea is to prepare 9 filler rows than append only the ones needed to reach the next tens, same idea of JChao, with a different implementation
With Filler AS (
SELECT n.n, replicate('9',94) nacha_rows
FROM (VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9)) n(n)
)
SELECT nacha_rows
FROM NACHA_TABLE
UNION ALL
SELECT nacha_rows
FROM Filler
OUTER APPLY (SELECT count(1) % 10 last
FROM NACHA_TABLE) l
WHERE filler.n + l.last <= 10
AND l.last > 0 -- to prevent filler line when NACHA_TABLE has exactly 10x rows
SQLFiddle demo
Looks like you need a dummy select statement:
select '1' as [col1], 'abcdef' as [col 2]
union all
select '2' as [col1], 'abcdef' as [col 2]
union all
select '3' as [col1], 'abcdef' as [col 2]
union all
select '4' as [col1], 'abcdef' as [col 2]
A way using a set of 10 rows and joining;
;with T(ord) as
(
select 1 as ord union all select ord + 1 from T where ord < 10
)
select isnull(nacha_rows, replicate('9', 94)) from T left join (
select
ROW_NUMBER() over (order by nacha_rows) row, nacha_rows
from NACHA_TABLE
) T2 on row = ord
Edit; Just realised that of course the table could have > 10 rows in the first place in which case this wont work.

Joining a list of values with table rows in SQL

Suppose I have a list of values, such as 1, 2, 3, 4, 5 and a table where some of those values exist in some column. Here is an example:
id name
1 Alice
3 Cindy
5 Elmore
6 Felix
I want to create a SELECT statement that will include all of the values from my list as well as the information from those rows that match the values, i.e., perform a LEFT OUTER JOIN between my list and the table, so the result would be like follows:
id name
1 Alice
2 (null)
3 Cindy
4 (null)
5 Elmore
How do I do that without creating a temp table or using multiple UNION operators?
If in Microsoft SQL Server 2008 or later, then you can use Table Value Constructor
Select v.valueId, m.name
From (values (1), (2), (3), (4), (5)) v(valueId)
left Join otherTable m
on m.id = v.valueId
Postgres also has this construction VALUES Lists:
SELECT * FROM (VALUES (1, 'one'), (2, 'two'), (3, 'three')) AS t (num,letter)
Also note the possible Common Table Expression syntax which can be handy to make joins:
WITH my_values(num, str) AS (
VALUES (1, 'one'), (2, 'two'), (3, 'three')
)
SELECT num, txt FROM my_values
With Oracle it's possible, though heavier From ASK TOM:
with id_list as (
select 10 id from dual union all
select 20 id from dual union all
select 25 id from dual union all
select 70 id from dual union all
select 90 id from dual
)
select * from id_list;
the following solution for oracle is adopted from this source. the basic idea is to exploit oracle's hierarchical queries. you have to specify a maximum length of the list (100 in the sample query below).
select d.lstid
, t.name
from (
select substr(
csv
, instr(csv,',',1,lev) + 1
, instr(csv,',',1,lev+1 )-instr(csv,',',1,lev)-1
) lstid
from (select ','||'1,2,3,4,5'||',' csv from dual)
, (select level lev from dual connect by level <= 100)
where lev <= length(csv)-length(replace(csv,','))-1
) d
left join test t on ( d.lstid = t.id )
;
check out this sql fiddle to see it work.
Bit late on this, but for Oracle you could do something like this to get a table of values:
SELECT rownum + 5 /*start*/ - 1 as myval
FROM dual
CONNECT BY LEVEL <= 100 /*end*/ - 5 /*start*/ + 1
... And then join that to your table:
SELECT *
FROM
(SELECT rownum + 1 /*start*/ - 1 myval
FROM dual
CONNECT BY LEVEL <= 5 /*end*/ - 1 /*start*/ + 1) mypseudotable
left outer join myothertable
on mypseudotable.myval = myothertable.correspondingval
Assuming myTable is the name of your table, following code should work.
;with x as
(
select top (select max(id) from [myTable]) number from [master]..spt_values
),
y as
(select row_number() over (order by x.number) as id
from x)
select y.id, t.name
from y left join myTable as t
on y.id = t.id;
Caution: This is SQL Server implementation.
fiddle
For getting sequential numbers as required for part of output (This method eliminates values to type for n numbers):
declare #site as int
set #site = 1
while #site<=200
begin
insert into ##table
values (#site)
set #site=#site+1
end
Final output[post above step]:
select * from ##table
select v.id,m.name from ##table as v
left outer join [source_table] m
on m.id=v.id
Suppose your table that has values 1,2,3,4,5 is named list_of_values, and suppose the table that contain some values but has the name column as some_values, you can do:
SELECT B.id,A.name
FROM [list_of_values] AS B
LEFT JOIN [some_values] AS A
ON B.ID = A.ID

SQL order by included character and string

I have a table and i want to colum joint_no column. The column's values are like these
FW-1
FW-2
.
.
.
FW-13
FW-R1
FW-1A
When i ordered them i get this results
FW-1
FW-10
FW-11
FW-12
FW-13
FW-1A
.
.
FW-R1
I want to get this result after sql query
FW-1
FW-1A
FW-2
FW-3
..
FW-13
FW-R1
can anybody help me?
If you can do it, I'd advise you to renumber the values so that the 'logical' order sticks to the alphabetical order. F-1 will then be updated to F-01, or F-001.
If you cannot do it, add a field that will be populated with the 'ordered' form of your code. You 'll then be able to order by the F-001 column and still display the F-1 value
Otherwise ordering your records will rapidly become your nightmare.
Using Patindex to find the first numeric expression as first sort field, then extracting the numeric part as integer as second sortfield and using the whole string as third sort field you might get the desired result.
Declare #a Table (c varchar(50))
Insert Into #a
Select 'FW-1'
Union Select 'FW-10'
Union Select 'FW-11'
Union Select 'FW-12'
Union Select 'FW-13'
Union Select 'FW-1A'
Union Select 'FW-2'
Union Select 'FW-3'
Union Select 'FW-R1'
Union Select 'FW-A1'
;With CTE as
(Select 1 as ID
Union All
Select ID + 1 from CTE where ID < 100
)
Select * from
(
Select c
,PATINDEX('%[0-9]%',c) as s1
,(Select Cast(
(Select Case
When SUBSTRING(c, ID, 1) LIKE '[0-9]'
Then SUBSTRING(c, ID, 1)
Else ''
End
From (Select * from CTE) AS X(ID)
Where ID <= LEN(c)
For XML PATH(''))
as int)
)
as s2
from
#a
) x
order by
s1,s2,c
With the output:
FW-1 4 1 -1
FW-1A 4 1 -1A
FW-2 4 2 -2
FW-3 4 3 -3
FW-10 4 10 -10
FW-11 4 11 -11
FW-12 4 12 -12
FW-13 4 13 -13
FW-A1 5 1 A1
FW-R1 5 1 R1
If the leading part is not fixed (FW-) you might need to add one additional sort field
Declare #a Table (c varchar(50))
Insert Into #a
Select 'FW-1'
Union Select 'FW-10'
Union Select 'FW-11'
Union Select 'FW-12'
Union Select 'FW-13'
Union Select 'FW-1A'
Union Select 'FW-2'
Union Select 'FW-3'
Union Select 'FW-R1'
Union Select 'FW-A1'
Union Select 'AB-A1'
Union Select 'AB-11'
;With CTE as
(Select 1 as ID
Union All
Select ID + 1 from CTE where ID < 100
)
Select * from
(
Select c
,SubString(c,1,PATINDEX('%[0-9]%',c)-1) as S0
,PATINDEX('%[0-9]%',c) as s1
,(Select Cast(
(Select Case
When SUBSTRING(c, ID, 1) LIKE '[0-9]'
Then SUBSTRING(c, ID, 1)
Else ''
End
From (Select * from CTE) AS X(ID)
Where ID <= LEN(c)
For XML PATH(''))
as int)
)
as s2
from
#a
) x
order by
s0,s1,s2,c