I treat bit shifts as movement within a declared type width. Vacant positions are filled with zero, and shifted-out bits don’t wrap around. Casting the input before shifting can change the available width.

Read the ordinary one-position shift
The first row starts with tinyint five. A left shift by one returns ten, while a right shift returns two. The same left shift on an int input also returns ten for this small value.
I’d keep the input type visible even when those results agree. Friendly low values don’t expose every width boundary. I’d explain a shift using its declared type. One example that fits several types doesn’t establish their shared boundary behavior.
WITH Inputs AS
(
SELECT CaseId,CAST(InputValue AS tinyint) AS InputValue,ShiftAmount
FROM (VALUES (1,5,1),(2,128,1),(3,5,-1),(4,5,9),
(5,CAST(NULL AS int),1)) v(CaseId,InputValue,ShiftAmount)
)
SELECT CaseId,InputValue,ShiftAmount,
LEFT_SHIFT(InputValue,ShiftAmount) AS TinyLeft,
RIGHT_SHIFT(InputValue,ShiftAmount) AS TinyRight,
LEFT_SHIFT(CAST(InputValue AS int),ShiftAmount) AS WideLeft
FROM Inputs
ORDER BY CaseId;

Expose a bit that leaves the type
The second tinyint value is 128, which has its highest bit set. Shifting it left by one removes that bit from the eight-bit result, leaving zero. The int version instead returns 256.
The wider cast occurs before LEFT_SHIFT, so the function operates within the wider input type. Casting the tiny result afterward wouldn’t restore the discarded bit. I’d retain this row when deciding whether the calculation intends a fixed-width representation or a wider numeric result.

Keep logical movement separate from rotation
Logical shifts fill vacant positions with zero. A shifted-out bit doesn’t reappear at the opposite end. That is different from a rotation operation that preserves the moved bit within the same width.
I wouldn’t use these functions as a substitute for rotation without a separate rule. The intended bit pattern matters more than whether the result looks numerically convenient. This example makes the width loss visible instead of hiding it behind a display-only representation.
Read a negative shift direction
The third row requests minus one. A negative amount shifts in the opposite direction. LEFT_SHIFT then returns the same value as a one-position right shift, while RIGHT_SHIFT moves left.
I’d keep that sign in the input column rather than describing the call only by its function name. A dynamic negative amount can reverse the intended direction. The argument contract should say whether negative requests are allowed or rejected by the application.
Test the amount against the width
A shift of nine exceeds the tinyint width, so both tiny results are zero. The int left shift can still retain the moved bits and returns 2560. The last row separately preserves NULL source input.
Those cases distinguish a known value shifted out of range from an unknown value. I’d retain both when validating a dynamic operation. Filling a NULL result with zero would erase a difference that the displayed inputs currently make clear.
Choose the supported interface deliberately
LEFT_SHIFT and RIGHT_SHIFT are available in SQL Server 2022 and later. This query uses their function forms and performs no writes. Each output’s type follows its source expression, including the explicitly widened int calculation.
I’d compare all five complete tuples on the target instance before reusing the expression. This small test doesn’t establish a business flag mapping or a general overflow policy. It states the movement, width and sign rules that the caller must use intentionally.
Name the type before you shift, and the bits stay where you expect.
A shift is not a rotation, it is movement that can discard bits outside the declared width.
Published by Pinal Dave on SQLAuthority. More of my work at pinaldave.com.
Discover more from SQL Authority with Pinal Dave
Subscribe to get the latest posts sent to your email.




