SQL Anywhere Error -824: Illegal reference to correlation name tableName - sql

When I run this script on Sybase IQ:
declare #YEAR int=2017
declare #MON int=6
declare #DAY int=7
update MainTable
set MainTable.Amount=(X.Number+Y.Number),
MainTable.Total=(X.Total+Y.Total)
from (select 'Number'= count(*), 'Total'=case when SUM(T1_Total) is null then 0 else SUM(T1_Total) end
from Table1
where T1_Account_NO=MainTable.Account_NO
and T1_SENTRY_YEAR=#YEAR and T1_SENTRY_MON=#MON and T1_SENTRY_DAY=#DAY) X,
(select 'Number'= count(*), 'Total'=case when SUM(T2_TOTAL) is null then 0 else SUM(T2_TOTAL) end
from Table2 where T2_Account_NO = MainTable.Account_NO
and T2_YEAR=#YEAR and T2_MON=#MON and T2_DAY=#DAY )Y
where MainTable.YEAR=#YEAR
and MainTable.MON = #MON
and MainTable.DAY=#DAY
I got an error like this : " SQL Anywhere Error -824: Illegal reference to correlation name MainTable"
How can I surpass this problem?

Have you tried adding MainTable to the from clause, eg:
update Maintable
set ...
from MainTable,
(select ... )X,
(select ... )Y
where ...
NOTE: I work with Sybase ASE, which does not allow 'external' correlation names to be referenced within derived tables, so I'm wondering if SQLAnywhere has a similar limitation ... ?
What happens if you pull the MainTable joins out to the top-most level of the query, eg:
declare #YEAR int=2017
declare #MON int=6
declare #DAY int=7
update MainTable
set MainTable.Amount=(X.Number+Y.Number),
MainTable.Total=(X.Total+Y.Total)
from (select T1_account_NO, 'Number'= count(*), 'Total'=case when SUM(T1_Total) is null then 0 else SUM(T1_Total) end
from Table1
where T1_SENTRY_YEAR=#YEAR and T1_SENTRY_MON=#MON and T1_SENTRY_DAY=#DAY
group by T1_Account_NO) X,
(select T2_Account_NO, 'Number'= count(*), 'Total'=case when SUM(T2_TOTAL) is null then 0 else SUM(T2_TOTAL) end
from Table2 where T2_YEAR=#YEAR and T2_MON=#MON and T2_DAY=#DAY
group by T2_Account_NO)Y
where MainTable.YEAR=#YEAR
and MainTable.MON = #MON
and MainTable.DAY=#DAY
and MainTable.Account_NO = X.T1_Account_NO
and MainTable.Account_NO = Y.T2_Account_NO
One potential performance-related downside would be if the derived tables now generate a large set of records that won't be joined with MainTable (unless the SQLAnywhere query engine is able to flatten the query in some way ... ???).
If this is an issue of not allowing 'external' correlation names in derived tables, another (obvious ?) solution would be to create a couple #temp tables from the results of joining MainTable with Table1/Table2, then perform the update of MainTable as a join with the #temp tables. [Possibly indexing the #temp tables if the data volumes are large enough to justify, performance-wise, the indexes.]

Did you try adding MainTable to the FROM clause?

I surpass this problem like this:
declare #YEAR int=2017
declare #MON int=6
declare #DAY int=7
update MainTable
set MainTable.Amount= (X.Number),
MainTable.Total = (X.Total)
from (select T1_Account_NO,'Number'= count(*), 'Total'=case when SUM(T1_Total) is null then 0 else SUM(T1_Total) end
from Table1
where T1_SENTRY_YEAR=#YEAR and T1_SENTRY_MON=#MON and T1_SENTRY_DAY=#DAY
group by T1_Account_NO) X,
where X.T1_Account_NO=MainTable.Account_NO
and MainTable.YEAR=#YEAR
and MainTable.MON = #MON
and MainTable.DAY=#DAY
update MainTable
set MainTable.Amount= coalesce(MainTable.Amount,0)+(Y.Number),
MainTable.Total = coalesce(MainTable.Total,0)+(Y.Total)
(select T2_Account_NO,'Number'= count(*), 'Total'=case when SUM(T2_TOTAL) is null then 0 else SUM(T2_TOTAL) end
from Table2
where T2_YEAR=#YEAR and T2_MON=#MON and T2_DAY=#DAY
group by T2_Account_NO) Y
where MainTable.YEAR=#YEAR
and MainTable.MON = #MON
and MainTable.DAY=#DAY
and Y.T2_Account_NO = MainTable.Account_NO
I have seperated update script the two parts.

Related

select COUNT and then condition SQL

I have a condition which based on my Count result, it should skip or include a join in my query,to make short the story,how to implement such a thing in SQL:
select count(names) as rslt
if(rslt)>0 then
select......join tables
else
Select...
as you see I want to say if the count is >0 then do the join otherwise it should skip then join and go to the next line,how should I achieve this?
DECLARE #Name INT
SELECT
#Name = COUNT(names)
FROM Table
IF #Name > 0
BEGIN
PRINT 'Do somthing'
END
ELSE
PRINT 'Do something else'
END
Just change the PRINT statements to your query logic
If you want to check if a resulting query returns any row (>0) then you should use an IF with an EXISTS rather than using COUNT. EXISTS will make the SQL engine stop running once it finds at least 1 row, while COUNT will force to actually count all records.
IF EXISTS (SELECT 1 FROM YourTable WHERE names IS NOT NULL)
BEGIN
SELECT
YourColumn
FROM
Table1
INNER JOIN Table2 ON --...
END
ELSE
BEGIN
SELECT
YourColumn
FROM
Table1
END
If on the other hand you need to check a specific amount, then you will have to COUNT and assign to variable.
DECLARE #CountTotal INT = (SELECT COUNT(names) FROM YourTable)
IF #CountTotal > 100
BEGIN
SELECT
YourColumn
FROM
Table1
INNER JOIN Table2 ON --...
END
ELSE
BEGIN
SELECT
YourColumn
FROM
Table1
END
DECLARE #reslt integer
#reslt = select count(names)
if #reslt >0 then
select......join tables
You need to put it in a variable and then call it.
You can also use dynamic query like below:
DECLARE #SQL NVARCHAR(MAX);
SELECT #SQL = N'SELECT *
FROM T1 ' + CASE WHEN (SELECT COUNT(names) FROM table1) > 0 THEN +
' INNER JOIN T2 ON T1.Id = T2.Id ' ELSE '' END
PRINT #SQL
EXEC sp_executesql #SQL;
Use CASE function, something like this :
Select count(CustomerID),
CASE
WHEN count(CustomerID) > 30 THEN "The quantity is greater than 30"
WHEN count(CustomerID) = 30 THEN "The quantity is 30"
ELSE "The quantity is something else"
END
FROM Customers;
You Can try below method as well.
if((select count(Name) from tableName)>0)
begin
select 1
end
else
begin
select 2
end
No need to use one temp variable to store the count .

Show 0 in count SQL

This is my result :
Year matches
2005 1
2008 2
and this is my expected result:
Year matches
2005 1
2006 0
2007 0
2008 2
This is what I have tried:
SELECT DATEPART(yy,A.match_date) AS [Year], COUNT(A.match_id) AS "matches"
FROM match_record A
INNER JOIN match_record B ON A.match_id = B.match_id
WHERE (score) IS NULL OR (score) = 0
GROUP BY DATEPART(yy,A.match_date);
I want to get zero as count in the years where score have some values(not null and zero, anything greater than 0) . Can someone help me?
This might do what you're looking for:
SELECT DATEPART(yy,A.match_date) AS [Year],
SUM(CASE WHEN score=0 or score is null THEN 1 ELSE 0 END) AS "matches"
FROM match_record A
INNER JOIN match_record B ON A.match_id = B.match_id
GROUP BY DATEPART(yy,A.match_date);
Assuming you have any data in the missing years, this should now produce your expected results.
If, instead, you need 0s for years where you have no data, you'll need to provide the list of years separately (say, via a numbers table) and then LEFT JOIN that source to your existing query.
Consider following is your table
SELECT * INTO #TEMP FROM
(
SELECT 2005 [YEARS],1 [MATCHES]
UNION ALL
SELECT 2008,2
)T
Declare two variables to get min and max date in your table
DECLARE #MINYEAR int;
DECLARE #MAXYEAR int;
SELECT #MINYEAR = MIN(YEARS) FROM #TEMP
SELECT #MAXYEAR = MAX(YEARS) FROM #TEMP
Do the following recursion to get years between the period in your table and LEFT JOIN with your table.
; WITH CTE as
(
select #MINYEAR as yr FROM #TEMP
UNION ALL
SELECT YR + 1
FROM CTE
WHERE yr < #MAXYEAR
)
SELECT DISTINCT C.YR,CASE WHEN T.MATCHES IS NULL THEN 0 ELSE T.MATCHES END MATCHES
FROM CTE C
LEFT JOIN #TEMP T ON C.yr=T.YEARS
DECLARE #t table(Year int, matches int)
DECLARE #i int=2005
WHILE #i <=2008
BEGIN
IF NOT exists (SELECT matches FROM tbl WHERE year=#i)
BEGIN
INSERT INTO #t
SELECT #i,'0'
SET #i=#i+1
END
else
BEGIN
INSERT INTO #t
SELECT year,[matches] from tbl
SET #i=#i+1
END
END
SELECT DISTINCT * FROM #t
how about,
SELECT
[year],
COUNT(*) [matches]
FROM (
SELECT
DATEPART(yy, [A].[match_date]) [year]
FROM
[match_record] [A]
LEFT JOIN
[match_record] [B]
ON [A].[match_id] = [B].[match_id]
WHERE
COALESCE([B].[score], 0) = 0) [Nils]
GROUP BY
[Year];

Sql server Query : Conditional Select columns with top keyword

DECLARE #mode INT;
SELECT CASE
WHEN #mode = 0
THEN t.Column1,Count(t.Column2) as Column2
ELSE top 1 t.Column1,Count(t.Column2) as Column2
END
FROM Table1 t
--Where some list of parameters
Group by t.Column1,t.Column2
Please read the above sql statement carefully. I have requirement to evaluate the query by two modes without changing the body of the query ie. From,Where and Group clauses should be written only once and not to replicate them (each one) anywhere in the result query
if #mode = 0 then the above said columns should be returned, and
if #mode <> 0 then the "Top1" of records should be returned
Both select conditions use the same given set list of parameters.
When I run the above query am facing the error "Incorrect syntax near the keyword 'top'." because we could not use the top 1 keyword within conditional select statements and select condition's columns must be matched even with their datatypes.
I need to fix the above query without affecting logic of the query.
IF(#mode <= 0)
BEGIN
Select Column1,Count(t.Column2) as Column2
From Table1 t
Group by t.Column1,t.Column2
END
Else
BEGIN
Select top 1 *
From Table1 t
END
Or create a temporary table and load data using where condition
Create Table #Temp (Column1 datetype,Column2 datetype)
Insert Into #Temp (Column1,Column2)
Select Column1,Column2
From Table1 t
Where condition
IF(#mode <= 0)
BEGIN
Select Column1,Count(t.Column2) as Column2
From #Temp t
Group by t.Column1,t.Column2
END
Else
BEGIN
Select top 1 *
From #Temp t
END
BEGIN
DECLARE #mode INT;
SET #mode = 0;
IF #mode <= 0
SELECT t.Column1,count(t.Column2) as Column2 from Table_Name t
GROUP BY t.Column1,t.Column2
ELSE
SELECT TOP 1 t.Column1,count(t.Column2) as Column2 from Table_Name t
GROUP BY t.Column1,t.Column2
END
DECLARE #mode INT;
Set #mode = 1 --for Min set of records
--Set #mode = 100 --for Max / full set of records
SELECT TOP (#var) PERCENT * t.Column1 ,Count(t.Column2) AS Column2 FROM Table1 t
--Where some list of parameters
GROUP BY t.Column1 ,t.Column2

count between two tables and finding difference

I currently have the following code which works. It's comparing two tables that are exactly the same but in two separate databases to ensure they have the same record count.
I was wondering if anyone saw a better way of achieving the below?
Declare #count1 int
Declare #count2 int
select #count1 = count(*) from database1.dbo.table1
select #count2 = count(*) from database2.dbo.table1
if #count1 <> #count2
begin
insert into log table saying counts don't matc
end
There's really no much better way. You can just do it without variables:
if (select count(*) from database1.dbo.table1) <> (select count(*) from database2.dbo.table1)
begin
insert into log table saying counts don't matc
end
If you want to know where the differences are you can use this to find the missing records in database 2
SELECT *
FROM database1.dbo.table1 D1
LEFT JOIN database2.dbo.table2 D2
ON D1.id = D2.id
WHERE D2.id IS NULL

SQL: Query timeout expired

I have a simple query for update table (30 columns and about 150 000 rows).
For example:
UPDATE tblSomeTable set F3 = #F3 where F1 = #F1
This query will affected about 2500 rows.
The tblSomeTable has a trigger:
ALTER TRIGGER [dbo].[trg_tblSomeTable]
ON [dbo].[tblSomeTable]
AFTER INSERT,DELETE,UPDATE
AS
BEGIN
declare #operationType nvarchar(1)
declare #createDate datetime
declare #UpdatedColumnsMask varbinary(500) = COLUMNS_UPDATED()
-- detect operation type
if not exists(select top 1 * from inserted)
begin
-- delete
SET #operationType = 'D'
SELECT #createDate = dbo.uf_DateWithCompTimeZone(CompanyId) FROM deleted
end
else if not exists(select top 1 * from deleted)
begin
-- insert
SET #operationType = 'I'
SELECT #createDate = dbo..uf_DateWithCompTimeZone(CompanyId) FROM inserted
end
else
begin
-- update
SET #operationType = 'U'
SELECT #createDate = dbo..uf_DateWithCompTimeZone(CompanyId) FROM inserted
end
-- log data to tmp table
INSERT INTO tbl1
SELECT
#createDate,
#operationType,
#status,
#updatedColumnsMask,
d.F1,
i.F1,
d.F2,
i.F2,
d.F3,
i.F3,
d.F4,
i.F4,
d.F5,
i.F5,
...
FROM (Select 1 as temp) t
LEFT JOIN inserted i on 1=1
LEFT JOIN deleted d on 1=1
END
And if I execute the update query I have a timeout.
How can I optimize a logic to avoid timeout?
Thank you.
This query:
SELECT *
FROM (
SELECT 1 AS temp
) t
LEFT JOIN
INSERTED i
ON 1 = 1
LEFT JOIN
DELETED d
ON 1 = 1
will yield 2500 ^ 2 = 6250000 records from a cartesian product of INSERTED and DELETED (that is all possible combinations of all records in both tables), which will be inserted into tbl1.
Is that what you wanted to do?
Most probably, you want to join the tables on their PRIMARY KEY:
INSERT
INTO tbl1
SELECT #createDate,
#operationType,
#status,
#updatedColumnsMask,
d.F1,
i.F1,
d.F2,
i.F2,
d.F3,
i.F3,
d.F4,
i.F4,
d.F5,
i.F5,
...
FROM INSERTED i
FULL JOIN
DELETED d
ON i.id = d.id
This will treat update to the PK as deleting a record and inserting another, with a new PK.
Thanks Quassnoi, It's a good idea with "FULL JOIN". It is helped me.
Also I try to update table in portions (1000 items in one time) to make my code works faster because for some companyId I need to update more than 160 000 rows.
Instead of old code:
UPDATE tblSomeTable set someVal = #someVal where companyId = #companyId
I use below one:
declare #rc integer = 0
declare #parts integer = 0
declare #index integer = 0
declare #portionSize int = 1000
-- select Ids for update
declare #tempIds table (id int)
insert into #tempIds
select id from tblSomeTable where companyId = #companyId
-- calculate amount of iterations
set #rc=##rowcount
set #parts = #rc / #portionSize + 1
-- update table in portions
WHILE (#parts > #index)
begin
UPDATE TOP (#portionSize) t
SET someVal = #someVal
FROM tblSomeTable t
JOIN #tempIds t1 on t1.id = t.id
WHERE companyId = #companyId
delete top (#portionSize) from #tempIds
set #index += 1
end
What do you think about this? Does it make sense? If yes, how to choose correct portion size?
Or simple update also good solution? I just want to avoid locks in the future.
Thanks