Add rows based on a column value - sql

I am having issues with creating additional rows based on a column value.
If my PageCount = 3 then I would need to have 2 additional rows where PONo is repeated but the ImagePath is incremented by 1 for each new row.
I am able to get the first row but, creating the additional rows with the ImagePath incremented by 1 is where I am stuck.
My result:
Expected result:
Finished: finished values
Current Select statement:
SELECT PO, CASE WHEN LEFT(u.Path,3)= 'M:\' THEN '\\ServerName\'+RIGHT(u.Path,LEN(u.Path)-3) ELSE u.Path END AS [Imagepath],PAGECOUNT
FROM OPENQUERY([LinkedServer],'select * from data.vw_purchasing_docs_unc') AS u INNER JOIN
OPENQUERY([LinkedServer],'select * from data.purchasing_docs') AS d ON u.docid=d.docid
WHERE (CONVERT(VARCHAR(10),d.STATUS_DATE,120)=CONVERT(VARCHAR(10),GETDATE(),120))
batch file:
bcp "select d.DOCID,DOC_TYPE,PO,d.STATUS, CASE WHEN LEFT(Path,3)= 'M:\' THEN '\\ServerName'+RIGHT(DWPath,LEN(Path)-3) ELSE Path END AS ImagePath, STATUS_DATE,'No' AS dwimport from openquery([LinkedServer],'select * from data.vw_purchasing_docs_unc') as u INNER JOIN openquery([LinkedServer],'select * from dwdata.purchasing_docs') as d ON u.docid=d.docid WHERE (CONVERT(varchar(10),STATUS_DATE,120)=CONVERT(varchar(10),GETDATE(),120)) AND d.STATUS IN ('FILED - Processing Complete','FILED - Partial Payment','FILED - Confirming') AND DOC_TYPE IN ('CO = Change Order','Purchase Order','CP = Capital Projects','Change Order','PO = Purchase Order','PO','PR = General Operating')" queryout "E:\Data\PO Trigger CSV\PO_Trigger_Doc.csv" -r \n -T -c -t"," -Umv -Smtvwrtst -Pm -q -k

Select ponumber,b.rplc,pagecount
from table t
cross apply
(select replace(imagepath,'f'+cast(n-1) as varchar(100),'f0') as rplc from numbers n where n<=t.pagecount)b
To create numbers table,if you are wondering why you need it.Look here
CREATE TABLE Number (N INT IDENTITY(1,1) PRIMARY KEY NOT NULL);
GO
INSERT INTO Number DEFAULT VALUES;
GO 10000
Using your select statement after update:
;With cte(ponumber,imagepath,pagecount)
as
SELECT PO, CASE WHEN LEFT(u.Path,3)= 'M:\' THEN '\\ServerName\'+RIGHT(u.Path,LEN(u.Path)-3) ELSE u.Path END AS [Imagepath],PAGECOUNT
FROM OPENQUERY([LinkedServer],'select * from data.vw_purchasing_docs_unc') AS u INNER JOIN
OPENQUERY([LinkedServer],'select * from data.purchasing_docs') AS d ON u.docid=d.docid
WHERE (CONVERT(VARCHAR(10),d.STATUS_DATE,120)=CONVERT(VARCHAR(10),GETDATE(),120))
)
select Ponumber,b.rplc,pagecount from cte c
cross apply
(select replace(imagepath,'f'+cast((n-1) as varchar(100)),'f0') as rplc from numbers n where n<=c.pagecount)b

If you would like to avoid additional table, you can use CTE:
WITH Images AS
(
SELECT * FROM (VALUES
('C:\Folder', 2),
('D:\Folder', 3)) T(ImagePath, Val)
), Numbers AS
(
SELECT * FROM (VALUES (1),(2),(3),(4)) T(N)
UNION ALL
SELECT N1.N*4+T.N N FROM (VALUES(1),(2),(3),(4)) T(N) CROSS JOIN Numbers N1
WHERE N1.N*4+T.N<=100
)
SELECT ImagePath + '\f' + CONVERT(nvarchar(10) ,ROW_NUMBER() OVER (PARTITION BY ImagePath ORDER BY (SELECT 1))) NewPath
FROM Images
CROSS APPLY (SELECT TOP(Val) * FROM Numbers) T(N)
Images is your source table. It can be anything, i.e. OPENQUERY. It produces:
NewPath
-------
C:\Folder\f1
C:\Folder\f2
D:\Folder\f1
D:\Folder\f2
D:\Folder\f3

Related

How do I return the count for both matching column in a single return

I'm trying to get a number of count for exist and non-exist for my validation to insert my data. As refer to the image below, I'm inserting 3 data in which 1 data consist of same ID number as destination table, 1 data consist of same quotation number as destination table and the last data is a new entry.
Okay here's my requirement. I am sending my source table data to insert into the dest table. So before inserting, I want to do validation by mapping the entire dest table like this:
SELECT COUNT(*) FROM sourceTable WHERE exists(
SELECT * FROM sourceTable WHERE QuotationId IN
(SELECT A.QuotationId FROM sourceTable A
JOIN DestTable B ON A.QuotationId = B.QuotationId
JOIN DestTable C ON A.QuotationNum = C.QuotationNum)
Without further details on table structure and so on it's difficult to tell, but something like the following might do the trick:
WITH cteMatch AS(
SELECT s.QuotationID AS src, d.QuotationID as dst
FROM sourceTable s
LEFT JOIN destTable d
)
SELECT CASE WHEN dst IS NULL THEN N'NonExist' ELSE N'Exist' END AS ValExist, COUNT(*) cnt
FROM cteMatch
GROUP BY CASE WHEN dst IS NULL THEN N'NonExist' ELSE N'Exist' END
You seem to want:
select count(*)
from sourcetable s
where exists(
select 1
from desttable d
where d.quotationid = s.quotationid or d.quotationnum = s.quotationnum
)
This counts how many rows in the source table have a quotation id or num that exists in the target table. If you want the count of both existing and non-existing rows, I would recommend:
select sum(flag) as cnt_exists, sum(1 - flag) as cnt_not_exists
from (
select
case when exists (
select 1
from desttable d
where d.quotationid = s.quotationid or d.quotationnum = s.quotationnum
) then 1 else 0 end as flag
from sourcetable s
) t

Select records where column has n character occurrences

I was wondering if this is possible in sqlite.
SELECT * FROM tbl WHERE substr_count(f, '*') = 5
It should return records that have 5 asterisks in the "f" column, like
a*b**c**
****a*
and so on
SELECT * FROM tbl WHERE length(f)-replace(f,'*','') = 5
This solution is easy if you have a tally or numbers table which simply contains a sequential list of integers. This would be a table you populated once but has many uses. With that you have:
Create Table Tally ( N int );
Insert Tally( N )
...
Select Z.<PrimaryKeyCol>, Sum( Z.Val )
From (
Select <PrimaryKeyCol>, 1 As Val
From tbl
Cross Join Tally As T
Where substr( tbl.f, T.N, 1 ) = '*'
) As Z
Group By Z.<PrimaryKeyCol>
Having Sum( Z.Val ) = 5

SQL query for finding first missing sequence string (prefix+no)

T-SQL query for finding first missing sequence string (prefix+no)
Sequence can have a prefix + a continuing no.
ex sequence will be
ID
-------
AUTO_500
AUTO_501
AUTO_502
AUTO_504
AUTO_505
AUTO_506
AUTO_507
AUTO_508
So above the missing sequence is AUTO_503 or if there is no missing sequence then it must return next sequence.
Also starting no is to specified ex. 500 in this case and prefix can be null i.e. no prefix only numbers as sequence.
You could LEFT JOIN the id numbers on shifted(+1) values to find gaps in sequential order:
SELECT
MIN(a.offsetnum) AS first_missing_num
FROM
(
SELECT 500 AS offsetnum
UNION
SELECT CAST(REPLACE(id, 'AUTO_', '') AS INT) + 1
FROM tbl
) a
LEFT JOIN
(SELECT CAST(REPLACE(id, 'AUTO_', '') AS INT) AS idnum FROM tbl) b ON a.offsetnum = b.idnum
WHERE
a.offsetnum >= 500 AND b.idnum IS NULL
SQLFiddle Demo
Using a recursive CTE to dynamically generate the sequence between the min and max of the ID Numbers maybe over complicated things a bit but it seems to work -
LIVE ON FIDDLE
CREATE TABLE tbl (
id VARCHAR(55)
);
INSERT INTO tbl VALUES
('AUTO_500'),
('AUTO_501'),
('AUTO_502'),
('AUTO_504'),
('AUTO_505'),
('AUTO_506'),
('AUTO_507'),
('AUTO_508'),
('509');
;WITH
data_cte(id)AS
(SELECT [id] = CAST(REPLACE(id, 'AUTO_', '') AS INT) FROM tbl)
,maxmin_cte(minId, maxId)AS
(SELECT [minId] = min(id),[maxId] = max(id) FROM data_cte)
,recursive_cte(n) AS
(
SELECT [minId] n from maxmin_cte
UNION ALL
SELECT (1 + n) n FROM recursive_cte WHERE n < (SELECT [maxId] from maxmin_cte)
)
SELECT x.n
FROM
recursive_cte x
LEFT OUTER JOIN data_cte y ON
x.n = y.id
WHERE y.id IS NULL
Check this solution.Here you just need to add identity column.
CREATE TABLE tbl (
id VARCHAR(55),
idn int identity(0,1)
);
INSERT INTO tbl VALUES
('AUTO_500'),
('AUTO_501'),
('AUTO_502'),
('AUTO_504'),
('AUTO_505'),
('AUTO_506'),
('AUTO_507'),
('AUTO_508'),
('509');
SELECT min(idn+500) FROM tbl where 'AUTO_'+cast((idn+500) as varchar)<>id
try this:
with cte as(
select cast(REPLACE(id,'AUTO_','') as int)-500+1 [diff],ROW_NUMBER()
over(order by cast(REPLACE(id,'AUTO_','') as int)) [rnk] from tbl)
select top 1 'AUTO_'+cast(500+rnk as varchar(50)) [ID] from cte
where [diff]=[rnk]
order by rnk desc
SQL FIddle Demo
Had a similar situation, where we have R_Cds that were like this R01005
;with Active_R_CD (R_CD)
As
(
Select Distinct Cast(Replace(R_CD,'R', ' ') as Int)
from table
where stat = 1)
select Arc.R_CD + 1 as 'Gaps in R Code'
from Active_R_CD as Arc
left outer join Active_R_CD as r on ARC.R_CD + 1 = R.R_CD
where R.R_CD is null
order by 1

SQL: how to get all the distinct characters in a column, across all rows

Is there an elegant way in SQL Server to find all the distinct characters in a single varchar(50) column, across all rows?
Bonus points if it can be done without cursors :)
For example, say my data contains 3 rows:
productname
-----------
product1
widget2
nicknack3
The distinct inventory of characters would be "productwigenka123"
Here's a query that returns each character as a separate row, along with the number of occurrences. Assuming your table is called 'Products'
WITH ProductChars(aChar, remain) AS (
SELECT LEFT(productName,1), RIGHT(productName, LEN(productName)-1)
FROM Products WHERE LEN(productName)>0
UNION ALL
SELECT LEFT(remain,1), RIGHT(remain, LEN(remain)-1) FROM ProductChars
WHERE LEN(remain)>0
)
SELECT aChar, COUNT(*) FROM ProductChars
GROUP BY aChar
To combine them all to a single row, (as stated in the question), change the final SELECT to
SELECT aChar AS [text()] FROM
(SELECT DISTINCT aChar FROM ProductChars) base
FOR XML PATH('')
The above uses a nice hack I found here, which emulates the GROUP_CONCAT from MySQL.
The first level of recursion is unrolled so that the query doesn't return empty strings in the output.
Use this (shall work on any CTE-capable RDBMS):
select x.v into prod from (values('product1'),('widget2'),('nicknack3')) as x(v);
Test Query:
with a as
(
select v, '' as x, 0 as n from prod
union all
select v, substring(v,n+1,1) as x, n+1 as n from a where n < len(v)
)
select v, x, n from a -- where n > 0
order by v, n
option (maxrecursion 0)
Final Query:
with a as
(
select v, '' as x, 0 as n from prod
union all
select v, substring(v,n+1,1) as x, n+1 as n from a where n < len(v)
)
select distinct x from a where n > 0
order by x
option (maxrecursion 0)
Oracle version:
with a(v,x,n) as
(
select v, '' as x, 0 as n from prod
union all
select v, substr(v,n+1,1) as x, n+1 as n from a where n < length(v)
)
select distinct x from a where n > 0
Given that your column is varchar, it means it can only store characters from codes 0 to 255, on whatever code page you have. If you only use the 32-128 ASCII code range, then you can simply see if you have any of the characters 32-128, one by one. The following query does that, looking in sys.objects.name:
with cteDigits as (
select 0 as Number
union all select 1 as Number
union all select 2 as Number
union all select 3 as Number
union all select 4 as Number
union all select 5 as Number
union all select 6 as Number
union all select 7 as Number
union all select 8 as Number
union all select 9 as Number)
, cteNumbers as (
select U.Number + T.Number*10 + H.Number*100 as Number
from cteDigits U
cross join cteDigits T
cross join cteDigits H)
, cteChars as (
select CHAR(Number) as Char
from cteNumbers
where Number between 32 and 128)
select cteChars.Char as [*]
from cteChars
cross apply (
select top(1) *
from sys.objects
where CHARINDEX(cteChars.Char, name, 0) > 0) as o
for xml path('');
If you have a Numbers or Tally table which contains a sequential list of integers you can do something like:
Select Distinct '' + Substring(Products.ProductName, N.Value, 1)
From dbo.Numbers As N
Cross Join dbo.Products
Where N.Value <= Len(Products.ProductName)
For Xml Path('')
If you are using SQL Server 2005 and beyond, you can generate your Numbers table on the fly using a CTE:
With Numbers As
(
Select Row_Number() Over ( Order By c1.object_id ) As Value
From sys.columns As c1
Cross Join sys.columns As c2
)
Select Distinct '' + Substring(Products.ProductName, N.Value, 1)
From Numbers As N
Cross Join dbo.Products
Where N.Value <= Len(Products.ProductName)
For Xml Path('')
Building on mdma's answer, this version gives you a single string, but decodes some of the changes that FOR XML will make, like & -> &.
WITH ProductChars(aChar, remain) AS (
SELECT LEFT(productName,1), RIGHT(productName, LEN(productName)-1)
FROM Products WHERE LEN(productName)>0
UNION ALL
SELECT LEFT(remain,1), RIGHT(remain, LEN(remain)-1) FROM ProductChars
WHERE LEN(remain)>0
)
SELECT STUFF((
SELECT N'' + aChar AS [text()]
FROM (SELECT DISTINCT aChar FROM Chars) base
ORDER BY aChar
FOR XML PATH, TYPE).value(N'.[1]', N'nvarchar(max)'),1, 1, N'')
-- Allow for a lot of recursion. Set to 0 for infinite recursion
OPTION (MAXRECURSION 365)

Can you create nested WITH clauses for Common Table Expressions?

WITH y AS (
WITH x AS (
SELECT * FROM MyTable
)
SELECT * FROM x
)
SELECT * FROM y
Does something like this work? I tried it earlier but I couldn't get it to work.
While not strictly nested, you can use common table expressions to reuse previous queries in subsequent ones.
To do this, the form of the statement you are looking for would be
WITH x AS
(
SELECT * FROM MyTable
),
y AS
(
SELECT * FROM x
)
SELECT * FROM y
You can do the following, which is referred to as a recursive query:
WITH y
AS
(
SELECT x, y, z
FROM MyTable
WHERE [base_condition]
UNION ALL
SELECT x, y, z
FROM MyTable M
INNER JOIN y ON M.[some_other_condition] = y.[some_other_condition]
)
SELECT *
FROM y
You may not need this functionality. I've done the following just to organize my queries better:
WITH y
AS
(
SELECT *
FROM MyTable
WHERE [base_condition]
),
x
AS
(
SELECT *
FROM y
WHERE [something_else]
)
SELECT *
FROM x
With does not work embedded, but it does work consecutive
;WITH A AS(
...
),
B AS(
...
)
SELECT *
FROM A
UNION ALL
SELECT *
FROM B
EDIT
Fixed the syntax...
Also, have a look at the following example
SQLFiddle DEMO
These answers are pretty good, but as far as getting the items to order properly, you'd be better off looking at this article
http://dataeducation.com/dr-output-or-how-i-learned-to-stop-worrying-and-love-the-merge
Here's an example of his query.
WITH paths AS (
SELECT
EmployeeID,
CONVERT(VARCHAR(900), CONCAT('.', EmployeeID, '.')) AS FullPath
FROM EmployeeHierarchyWide
WHERE ManagerID IS NULL
UNION ALL
SELECT
ehw.EmployeeID,
CONVERT(VARCHAR(900), CONCAT(p.FullPath, ehw.EmployeeID, '.')) AS FullPath
FROM paths AS p
JOIN EmployeeHierarchyWide AS ehw ON ehw.ManagerID = p.EmployeeID
)
SELECT * FROM paths order by FullPath
we can create nested cte.please see the below cte in example
;with cte_data as
(
Select * from [HumanResources].[Department]
),cte_data1 as
(
Select * from [HumanResources].[Department]
)
select * from cte_data,cte_data1
I was trying to measure the time between events with the exception of what one entry that has multiple processes between the start and end. I needed this in the context of other single line processes.
I used a select with an inner join as my select statement within the Nth cte. The second cte I needed to extract the start date on X and end date on Y and used 1 as an id value to left join to put them on a single line.
Works for me, hope this helps.
cte_extract
as
(
select ps.Process as ProcessEvent
, ps.ProcessStartDate
, ps.ProcessEndDate
-- select strt.*
from dbo.tbl_some_table ps
inner join (select max(ProcessStatusId) ProcessStatusId
from dbo.tbl_some_table
where Process = 'some_extract_tbl'
and convert(varchar(10), ProcessStartDate, 112) < '29991231'
) strt on strt.ProcessStatusId = ps.ProcessStatusID
),
cte_rls
as
(
select 'Sample' as ProcessEvent,
x.ProcessStartDate, y.ProcessEndDate from (
select 1 as Id, ps.Process as ProcessEvent
, ps.ProcessStartDate
, ps.ProcessEndDate
-- select strt.*
from dbo.tbl_some_table ps
inner join (select max(ProcessStatusId) ProcessStatusId
from dbo.tbl_some_table
where Process = 'XX Prcss'
and convert(varchar(10), ProcessStartDate, 112) < '29991231'
) strt on strt.ProcessStatusId = ps.ProcessStatusID
) x
left join (
select 1 as Id, ps.Process as ProcessEvent
, ps.ProcessStartDate
, ps.ProcessEndDate
-- select strt.*
from dbo.tbl_some_table ps
inner join (select max(ProcessStatusId) ProcessStatusId
from dbo.tbl_some_table
where Process = 'YY Prcss Cmpltd'
and convert(varchar(10), ProcessEndDate, 112) < '29991231'
) enddt on enddt.ProcessStatusId = ps.ProcessStatusID
) y on y.Id = x.Id
),
.... other ctes
Nested 'With' is not supported, but you can always use the second With as a subquery, for example:
WITH A AS (
--WITH B AS ( SELECT COUNT(1) AS _CT FROM C ) SELECT CASE _CT WHEN 1 THEN 1 ELSE 0 END FROM B --doesn't work
SELECT CASE WHEN count = 1 THEN 1 ELSE 0 END AS CT FROM (SELECT COUNT(1) AS count FROM dual)
union all
select 100 AS CT from dual
)
select CT FROM A