I need to understand the following MS SQL statement ( SELECT FROM VALUES) - sql-server

I am pretty new to MS SQL but I am having to work with it a lot now. I need to understand what is going on here:
BEGIN TRANSACTION loadHalfdayAbsences;
INSERT INTO #halfDayAbsences
([AbsencePart],
[AbsenceId],
[DeleteDate],
[LastChangeDate]
)
SELECT CASE ap.AbsencePart
WHEN 1
THEN a.AbsenceStart
WHEN 3
THEN a.AbsenceEnd
ELSE CASE
WHEN a.AbsenceStartHalfDay = 1
THEN DATEADD(DAY, 1, a.AbsenceStart)
ELSE a.AbsenceStart
END
END AS newEnd,
CASE ap.AbsencePart
WHEN 1
THEN 0.50
WHEN 2
THEN 1.00
WHEN 3
THEN 0.50
END AS newDuration,
[ap].[AbsencePart],
[a].[AbsenceId],
[a].[EmployeeId],
FROM
(
SELECT AbsencePart
FROM(VALUES(1), (2), (3)) AS t(AbsencePart)
) AS ap
INNER JOIN dwh.Absence AS a ON 1 = 1
WHERE AbsenceType IN
(
SELECT AbsenceType
FROM #AbsenceType4TimeTac
)
Particularly:
FROM
(
SELECT AbsencePart
FROM(VALUES(1), (2), (3)) AS t(AbsencePart)
) AS ap
INNER JOIN dwh.Absence AS a ON 1 = 1
WHERE AbsenceType IN
(
SELECT AbsenceType
FROM #AbsenceType4TimeTac
)
What does this part do?
What do the VALUES do here for me?
What happens to the data?
Is it being selected, but only fields where there is either a 1, a 2 or a 3 in there? Or what is going on with VALUES?
Thanks for any input in advance :)

The portion
FROM (VALUES(1), (2), (3)) AS t(AbsencePart)
just is an inline table consisting of three values, 1 through 3, in a column called AbsensePart. You could have also used the following syntax:
FROM
(
SELECT 1 AS AbsencePart UNION ALL
SELECT 2 UNION ALL
SELECT 3
) t

That is known as a Table Value Constructor. It is used instead of things like temp tables when you have a list of known values you want in a table. You can read more about them here.

Related

How can I select individual rows related to multiple Ids?

I need to select related Ids from a table based on a list of provided Ids - effectively an Adjacency List problem. I have a working query for a single Id, but it is frankly inelegant at best even though it works! I would welcome suggestions for improvements and for ways to move the single Id solution to a multiple Id solution.
I have a database table like so:
CREATE TABLE [BookingLines]
(
[BookingLineId] BIGINT NOT NULL IDENTITY (138, 1),
[BookingId] BIGINT NOT NULL,
---- Additional Columns Redacted for brevity
[ContractNumber] INT NOT NULL DEFAULT 0,
[ContractSubNumber] DECIMAL NOT NULL DEFAULT 0,
---- Additional Columns Redacted for brevity
);
There will be records in this table, and in some cases there will be 1 or more pairs of records relating to the same Booking Id. The differentiation is in the ContractSubNumber column, where one value in the pair will be n.0 and the other n.1. So if there were three consecutive pairs, the Contract SubNumbers would be:
LineId BookingId SubNumber
1 1 0.0
2 1 0.1
3 1 1.0
4 1 1.1
5 1 2.0
6 1 2.1
I may need to start from the Line Id representing either of the sub numbers, and collect the opposing one. So, if I am starting from LineId 1, I need to retrieve LineId 2 being the related row. I can do this on a single Id using multiple sub selects, like this:
SELECT BookingLineId
FROM
(
SELECT BookingLineId
FROM BookingLines
WHERE BookingId = 1
AND FLOOR(ContractSubNumber) =
(
SELECT FLOOR(ContractSubNumber)
FROM BookingLines
WHERE BookingId = 1 AND BookingLineId = (1)
)
)
WHERE BookingLineId <> 1;
This works correctly, returning the value 2 in this case.
How can I make this more elegant and efficient?
How can I rewrite this to return the opposing values of all Ids in a specified list e.g.
WHERE BookingId = 1 AND BookingLineId IN (1,3,5))
and have it return the result 2,4,6?
All suggestions gratefully received.
EDIT
I have corrected the typo in the SQL provided in the original question, and using the framework proposed by #McNets this is the solution I went for:
SELECT BL.BookingLineId
FROM BookingLines BL
INNER JOIN BookingLines ABL ON ABL.BookingId = BL.BookingId
AND ABL.BookingLineId IN (22, 24, 26)
AND FLOOR(BL.ContractSubNumber) = FLOOR(ABL.ContractSubNumber)
WHERE BL.BookingId = 3 AND BL.BookingLineId NOT IN (22,24,26);
I am very grateful for the contributions and for the final answer. Thanks guys!
As far as there is no information about AgencyBookingLines and no sample data I cannot set up a fiddle example, but I think you can move the AgencyBookingLines subquery to the ON clause.
SELECT BL.BookingLineId
FROM BookingLines BL
INNER JOIN AgencyBookingLines ABL
ON ABL.BookingId = BL.BookinId
AND ABL.BookingLineId = 1
AND FLOOR(BL.ContractSubNumber) = FLOOR(ABL.ContractSubNumber
WHERE BL.BookingId = 1
AND BL.BookingLineId <> 1;
--
-- AND BL.BookingLineId IN (2,4,6);
Will it sub numbers always *.0 & *.1. Then you could try the below
SELECT oppo.*
FROM AgencyBookingLines AS main
INNER JOIN AgencyBookingLines AS oppo ON
oppo.BookingId = main.BookingId
AND oppo.SubNumber <> main.SubNumber
AND FLOOR(oppo.SubNumber) = FLOOR(main.SubNumber)
WHERE main.BookingId = 1
AND main.LineId IN (1,3,5)

Inserting 2 insert query records in SQL server

need your expert
when inserting query like this :
use tabelmantap
delete from T_CekUser
insert into T_CekUser(T_21SI)
select tbla.MASA
from
(
select * from T_21SI
union
select * from T_21SI
) as tbla
where tbla.THN_DATA = '2014' and tbla.KEY_NPWP = '01.576.555.5-123.000'
insert into T_CekUser(T_23SI)
select tblb.MASA from
(
select * from T_23SI
union
select * from T_23SI
) as tblb
where tblb.THN_DATA = '2014' and tblb.KEY_NPWP = '01.576.555.5-123.000'
all that query is in one line/page query in sql server
and i get results like this :
T_21SI | T_23SI
1 null
2 null
3 null
4 null
null 2
null 3
been tired, looking around for answer or solution as i wanna be like this :
T_21SI | T_23SI
1 2
2 3
3 null
4 null
null null
null null
all file type are int
i have made insert using select from two tables (using inner join) but result is not being expected
any help would be really appreciated..thanks
Try using the PIVOT statement details are here. https://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx

T-SQL select value where value contains less than 3 of the declared characters

Im trying to write a select statement which returns the value if it doesnt have at least 3 of the declared characters but I cant think of how to get it working, can someone point me in the right direction?
One thing to consider, I am not allowed to create a temporary table for this exercise.
I havn't really got any SQL so far as I cant think of a way to do it without a temp table.
the declared characters are any alpha characters between a and z, so if the value in the db is '1873' then it would return the value because it doesnt have at least 3 of the declared characters, but if the value was 'abcdefg' then it would not be returned as it has at least 3 of the declared characters.
Is anyone able to point me in a starting direction for this?
This will find all sys.objects with an x or a z:
Some explanations, as this is an exercise and you want to learn something:
You can split a delimitted string by transforming it into XML. x,z comes out as <x>x</x><x>z</x>. You can use this to create a derived table.
I use a CTE to avoid a created or declared table...
You can use CROSS APPLY for row-wise actions. Here I use CHARINDEX to find the position(s) of the chars you are looking for.
If all of them are not found, there SUM is zero. I use GROUP BY and HAVING to check this.
Hope this is clear :-)
DECLARE #chars VARCHAR(100)='x,z';
WITH Splitted AS
(
SELECT A.B.value('.','char') AS TheChar
FROM
(
SELECT CAST('<x>' + REPLACE(#chars,',','</x><x>')+ '</x>' AS XML) AS AsXml
) AS tbl
CROSS APPLY AsXml.nodes('/x') AS A(B)
)
SELECT name
FROM sys.objects
CROSS APPLY (SELECT CHARINDEX(TheChar,name) AS Found FROM Splitted) AS Found
GROUP BY name,Found
HAVING SUM(Found)>0
With
SrcTab As (
Select *
From (values ('Contains x y z')
, ('Contains x and y')
, ('Contains y only')) v (SrcField)),
CharList As ( --< CTE instead of temporary table
Select *
From (values ('x')
, ('y')
, ('z')) v (c))
Select SrcField
From SrcTab, CharList
Group By SrcField
Having SUM(SIGN(CharIndex(C, SrcField))) < 3 --< Count hits
;
If Distinct is not desirable and we need to only check count for each row:
With
SrcTab As ( --< Sample Data CTE
Select *
From (values ('Contains x y z')
, ('Contains x and y')
, ('Contains y only')
, ('Contains y only')) v (SrcField))
Select SrcField
From SrcTab
Where (
Select Count(*) --< Count hits
From (Values ('x'), ('y'), ('z')) v (c)
Where CharIndex(C, SrcField) > 0
) < 3
;
Using Numbers Table and Joins..I used declared characters as only 4 for demo purposes
Input:
12345
abcdef
ab
Declared table:used only 3 for demo..
a
b
c
Output:
12345
ab
Demo:
---Table population Scripts
Create table #t
(
val varchar(20)
)
insert into #t
select '12345'
union all
select 'abcdef'
union all
select 'ab'
create table #declarecharacters
(
dc char(1)
)
insert into #declarecharacters
select 'a'
union all
select 'b'
union all
select 'c'
Query used
;with cte
as
(
select * from #t
cross apply
(
select substring(val,n,1) as strr from numbers where n<=len(val))b(outputt)
)
select val from
cte c
left join
#declarecharacters dc1
on
dc1.dc=c.outputt
group by val
having
sum(case when dc is null then 0 else 1 end ) <3

Displaying sorted hierarchy rows in SQL server?

Assuming I have this table : ( c is a child of parent p)
c p
------
40 0
2 3
2 40
3 1
7 2
1 0
Where (0 means root) — I want the order of select to be displayed as :
c b
------
1 0
3 1
2 3
40 0
2 40
7 2
That's becuase we have 2 roots (1,40) and 1 < 40.
So we start at 1 and then display below it - all it's descendants.
Then we get to 40. same logic again.
Question:
How can I do it ?
I've succeeded to display it recursively + finding level of hierarchy*(not sure if it helps though)*
WITH cte(c, p) AS (
SELECT 40, 0 UNION ALL
SELECT 2,3 UNION ALL
SELECT 2,40 UNION ALL
SELECT 3,1 UNION ALL
SELECT 7,2 UNION ALL
SELECT 1,0
) , cte2 AS(
SELECT c,
p,
PLevel = 1
FROM cte
WHERE p = 0
UNION ALL
SELECT cte.c,
cte.p,
PLevel = cte2.PLevel + 1
FROM cte
INNER JOIN cte2
ON cte2.c = cte.p
)
SELECT *
FROM cte2
Full SQL fiddle
You have almost done it. Just add a rank to identify each group and then sort the data on it.
Also, as you are working with more complex hierarchy we need to change the [level] value. In is now not a number, put the full path of the current element to its parent. Where \ means parent. For example the following string:
\1\5\4\1
represents the hierarchy below:
1
--> 5
--> 4
--> 1
I get the idea from hierarchyid type. You may want to consider storing hierarchies using it, as it has handy build-in functions for working with such structures.
Here is full working example with the new data:
DECLARE #DataSource TABLE
(
[c] TINYINT
,[p] TINYINT
);
INSERT INTO #DataSource ([c], [p])
VALUES (1,0)
,(3, 1)
,(2, 3)
,(5,1)
,(7, 2)
,(40, 0)
,(2, 40);
WITH DataSource ([c], [p], [level], [rank])AS
(
SELECT [c]
,[p]
,CAST('/' AS VARCHAR(24))
,ROW_NUMBER() OVER (ORDER BY [c] ASC)
FROM #DataSource
WHERE [p] = 0
UNION ALL
SELECT DS.[c]
,DS.[p]
,CAST(DS1.[level] + CAST(DS.[c] AS VARCHAR(3)) + '/' AS VARCHAR(24))
,DS1.[rank]
FROM #DataSource DS
INNER JOIN DataSource DS1
ON DS1.[c] = DS.[p]
)
SELECT [c]
,[p]
FROM DataSource
ORDER BY [Rank]
,CAST([level] AS hierarchyid);
Again, pay attention to the node (7,2) which is participating in the two groups (even in your example). I guess this is just a sample data and you have a way to defined where the node should be included.

Get the missing value in a sequence of numbers

I made the following query for the SQL Server backend
SELECT TOP(1) (v.rownum + 99)
FROM
(
SELECT incrementNo-99 as id, ROW_NUMBER() OVER (ORDER BY incrementNo) as rownum
FROM proposals
WHERE [year] = '12'
) as v
WHERE v.rownum <> v.id
ORDER BY v.rownum
to find the first unused proposal number.
(It's not about the lastrecord +1)
But I realized ROW_NUMBER is not supported in access.
I looked and I can't find something similar.
Does anyone know how to get the same result as a ROW_NUMBER in access?
Maybe there's a better way of doing this.
Actually people insert their proposal No (incrementID) with no constraint. This number looks like this 13-152. xx- is for the current year and the -xxx is the proposal number. The last 3 digits are supposed to be incremental but in some case maybe 10 times a year they have to skip some numbers. That's why I can't have the auto increment.
So I do this query so when they open the form, the default number is the first unused.
How it works:
Because the number starts at 100, I do -99 so it starts at 1.
Then I compare the row number with the id so it looks like this
ROW NUMBER | ID
1 1 (100)
2 2 (101)
3 3 (102)
4 5 (104)<--------- WRONG
5 6 (105)
So now I know that we skip 4. So I return (4 - 99) = 103
If there's a better way, I don't mind changing but I really like this query.
If there's really no other way and I can't simulate a row number in access, i will use the pass through query.
Thank you
From your question it appears that you are looking for a gap in a sequence of numbers, so:
SELECT b.akey, (
SELECT Top 1 akey
FROM table1 a
WHERE a.akey > b.akey) AS [next]
FROM table1 AS b
WHERE (
SELECT Top 1 akey
FROM table1 a
WHERE a.akey > b.akey) <> [b].[akey]+1
ORDER BY b.akey
Where table1 is the table and akey is the sequenced number.
SELECT T.Value, T.next -1 FROM (
SELECT b.Value , (
SELECT Top 1 Value
FROM tblSequence a
WHERE a.Value > b.Value) AS [next]
FROM tblSequence b
) T WHERE T.next <> T.Value +1

Resources