SQL Query - find with all NULLS - sql

I have the following table that has Usename and Site information:
Username Site
jbrown NULL
jbrown NULL
jbrown NULL
msmith 3
msmith 12
msmith NULL
ptodd 18
ptodd 16
ptodd NULL
jdrem 3
jdrem NULL
jdrem NULL
What I need to do is fetch usernames that have ALL NULLS for their site or ones that ANY Site that is 3. So in this case the output would be:
jbrown
msmith
jdrem
Note that ptodd was not in the end result as their were no sites that had 3.
I was considering a group by but not sure how to say ALL NULLS or ANY that are 3 based on Username.

You can use group by and conditional aggregation.
select username
from tablename
group by username
having count(*)=count(case when site is null then 1 end)
or count(case when site=3 then 1 end)>=1

I guess this might be too similar to other to count but..
SELECT [Username]
FROM Table
GROUP BY [Username]
HAVING MAX([Site]) IS NULL OR
COUNT(CASE WHEN [Site] = 3 THEN 1 END) > 0

Conditional aggregation will probably be faster, but here is another option:
using not exists(...) or Site = 3:
select distinct Username
from t
where not exists (
select 1
from t as i
where i.username = t.username
and i.site is not null
)
or Site = 3

I like to use what I call the "better than" sql principal. Where you make sure there is nothing "better than".
select username, site
from table_name t
left join table_name betterThan on betterThan.site is not null and betterThan.username = t.username and betterThan.id <> t.id
where (t.site is null or t.site = 3) and betterThan.id is null;

Related

Sum totals in new columns per period range SQL [duplicate]

If I have a MySQL table looking something like this:
company_name action pagecount
-------------------------------
Company A PRINT 3
Company A PRINT 2
Company A PRINT 3
Company B EMAIL
Company B PRINT 2
Company B PRINT 2
Company B PRINT 1
Company A PRINT 3
Is it possible to run a MySQL query to get output like this:
company_name EMAIL PRINT 1 pages PRINT 2 pages PRINT 3 pages
-------------------------------------------------------------
CompanyA 0 0 1 3
CompanyB 1 1 2 0
The idea is that pagecount can vary so the output column amount should reflect that, one column for each action/pagecount pair and then number of hits per company_name. I'm not sure if this is called a pivot table but someone suggested that?
This basically is a pivot table.
A nice tutorial on how to achieve this can be found here: http://www.artfulsoftware.com/infotree/qrytip.php?id=78
I advise reading this post and adapt this solution to your needs.
Update
After the link above is currently not available any longer I feel obliged to provide some additional information for all of you searching for mysql pivot answers in here. It really had a vast amount of information, and I won't put everything from there in here (even more since I just don't want to copy their vast knowledge), but I'll give some advice on how to deal with pivot tables the sql way generally with the example from peku who asked the question in the first place.
Maybe the link comes back soon, I'll keep an eye out for it.
The spreadsheet way...
Many people just use a tool like MSExcel, OpenOffice or other spreadsheet-tools for this purpose. This is a valid solution, just copy the data over there and use the tools the GUI offer to solve this.
But... this wasn't the question, and it might even lead to some disadvantages, like how to get the data into the spreadsheet, problematic scaling and so on.
The SQL way...
Given his table looks something like this:
CREATE TABLE `test_pivot` (
`pid` bigint(20) NOT NULL AUTO_INCREMENT,
`company_name` varchar(32) DEFAULT NULL,
`action` varchar(16) DEFAULT NULL,
`pagecount` bigint(20) DEFAULT NULL,
PRIMARY KEY (`pid`)
) ENGINE=MyISAM;
Now look into his/her desired table:
company_name EMAIL PRINT 1 pages PRINT 2 pages PRINT 3 pages
-------------------------------------------------------------
CompanyA 0 0 1 3
CompanyB 1 1 2 0
The rows (EMAIL, PRINT x pages) resemble conditions. The main grouping is by company_name.
In order to set up the conditions this rather shouts for using the CASE-statement. In order to group by something, well, use ... GROUP BY.
The basic SQL providing this pivot can look something like this:
SELECT P.`company_name`,
COUNT(
CASE
WHEN P.`action`='EMAIL'
THEN 1
ELSE NULL
END
) AS 'EMAIL',
COUNT(
CASE
WHEN P.`action`='PRINT' AND P.`pagecount` = '1'
THEN P.`pagecount`
ELSE NULL
END
) AS 'PRINT 1 pages',
COUNT(
CASE
WHEN P.`action`='PRINT' AND P.`pagecount` = '2'
THEN P.`pagecount`
ELSE NULL
END
) AS 'PRINT 2 pages',
COUNT(
CASE
WHEN P.`action`='PRINT' AND P.`pagecount` = '3'
THEN P.`pagecount`
ELSE NULL
END
) AS 'PRINT 3 pages'
FROM test_pivot P
GROUP BY P.`company_name`;
This should provide the desired result very fast. The major downside for this approach, the more rows you want in your pivot table, the more conditions you need to define in your SQL statement.
This can be dealt with, too, therefore people tend to use prepared statements, routines, counters and such.
Some additional links about this topic:
http://anothermysqldba.blogspot.de/2013/06/pivot-tables-example-in-mysql.html
http://www.codeproject.com/Articles/363339/Cross-Tabulation-Pivot-Tables-with-MySQL
http://datacharmer.org/downloads/pivot_tables_mysql_5.pdf
https://codingsight.com/pivot-tables-in-mysql/
My solution is in T-SQL without any pivots:
SELECT
CompanyName,
SUM(CASE WHEN (action='EMAIL') THEN 1 ELSE 0 END) AS Email,
SUM(CASE WHEN (action='PRINT' AND pagecount=1) THEN 1 ELSE 0 END) AS Print1Pages,
SUM(CASE WHEN (action='PRINT' AND pagecount=2) THEN 1 ELSE 0 END) AS Print2Pages,
SUM(CASE WHEN (action='PRINT' AND pagecount=3) THEN 1 ELSE 0 END) AS Print3Pages
FROM
Company
GROUP BY
CompanyName
For MySQL you can directly put conditions in SUM() function and it will be evaluated as Boolean 0 or 1 and thus you can have your count based on your criteria without using IF/CASE statements
SELECT
company_name,
SUM(action = 'EMAIL')AS Email,
SUM(action = 'PRINT' AND pagecount = 1)AS Print1Pages,
SUM(action = 'PRINT' AND pagecount = 2)AS Print2Pages,
SUM(action = 'PRINT' AND pagecount = 3)AS Print3Pages
FROM t
GROUP BY company_name
DEMO
For dynamic pivot, use GROUP_CONCAT with CONCAT.
The GROUP_CONCAT function concatenates strings from a group into one string with various options.
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(CASE WHEN action = "',
action,'" AND ',
(CASE WHEN pagecount IS NOT NULL
THEN CONCAT("pagecount = ",pagecount)
ELSE pagecount IS NULL END),
' THEN 1 ELSE 0 end) AS ',
action, IFNULL(pagecount,'')
)
)
INTO #sql
FROM
t;
SET #sql = CONCAT('SELECT company_name, ', #sql, '
FROM t
GROUP BY company_name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
DEMO HERE
A stardard-SQL version using boolean logic:
SELECT company_name
, COUNT(action = 'EMAIL' OR NULL) AS "Email"
, COUNT(action = 'PRINT' AND pagecount = 1 OR NULL) AS "Print 1 pages"
, COUNT(action = 'PRINT' AND pagecount = 2 OR NULL) AS "Print 2 pages"
, COUNT(action = 'PRINT' AND pagecount = 3 OR NULL) AS "Print 3 pages"
FROM tbl
GROUP BY company_name;
db<>fiddle here
Old sqlfiddle
How?
TRUE OR NULL yields TRUE.
FALSE OR NULL yields NULL.
NULL OR NULL yields NULL.
And COUNT only counts non-null values. Voilá.
Correct answer is:
select table_record_id,
group_concat(if(value_name='note', value_text, NULL)) as note
,group_concat(if(value_name='hire_date', value_text, NULL)) as hire_date
,group_concat(if(value_name='termination_date', value_text, NULL)) as termination_date
,group_concat(if(value_name='department', value_text, NULL)) as department
,group_concat(if(value_name='reporting_to', value_text, NULL)) as reporting_to
,group_concat(if(value_name='shift_start_time', value_text, NULL)) as shift_start_time
,group_concat(if(value_name='shift_end_time', value_text, NULL)) as shift_end_time
from other_value
where table_name = 'employee'
and is_active = 'y'
and is_deleted = 'n'
GROUP BY table_record_id
There is a tool called MySQL Pivot table generator, it can help you create a web-based pivot table that you can later export to excel(if you like). it can work if your data is in a single table or in several tables.
All you need to do is to specify the data source of the columns (it supports dynamic columns), rows, the values in the body of the table, and table relationship (if there are any)
The home page of this tool is https://mysqlreports.com/mysql-reporting-tools/mysql-pivot-table/
select t3.name, sum(t3.prod_A) as Prod_A, sum(t3.prod_B) as Prod_B, sum(t3.prod_C) as Prod_C, sum(t3.prod_D) as Prod_D, sum(t3.prod_E) as Prod_E
from
(select t2.name as name,
case when t2.prodid = 1 then t2.counts
else 0 end prod_A,
case when t2.prodid = 2 then t2.counts
else 0 end prod_B,
case when t2.prodid = 3 then t2.counts
else 0 end prod_C,
case when t2.prodid = 4 then t2.counts
else 0 end prod_D,
case when t2.prodid = "5" then t2.counts
else 0 end prod_E
from
(SELECT partners.name as name, sales.products_id as prodid, count(products.name) as counts
FROM test.sales left outer join test.partners on sales.partners_id = partners.id
left outer join test.products on sales.products_id = products.id
where sales.partners_id = partners.id and sales.products_id = products.id group by partners.name, prodid) t2) t3
group by t3.name ;
One option would be combining use of CASE..WHEN statement is redundant within an aggregation for MySQL Database, and considering the needed query generation dynamically along with getting proper column title for the result set as in the following code block :
SET #sql = NULL;
SELECT GROUP_CONCAT(
CONCAT('SUM( `action` = ''', action, '''',pc0,' ) AS ',action,pc1)
)
INTO #sql
FROM
(
SELECT DISTINCT `action`,
IF(`pagecount` IS NULL,'',CONCAT('page',`pagecount`)) AS pc1,
IF(`pagecount` IS NULL,'',CONCAT(' AND `pagecount` = ', pagecount, '')) AS pc0
FROM `tab`
ORDER BY CONCAT(action,pc0)
) t;
SET #sql = CONCAT('SELECT company_name,',#sql,' FROM `tab` GROUP BY company_name');
SELECT #sql;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Demo
SELECT company_name, SUM(CASE WHEN ACTION = 'Email' THEN 1 ELSE 0 END) AS "Email",
SUM(CASE WHEN ACTION = 'Print' AND pagecount = 1 THEN 1 ELSE 0 END) AS "print 1 PAGE",
SUM(CASE WHEN ACTION = 'Print' AND pagecount = 2 THEN 1 ELSE 0 END) AS "print 2 PAGE",
SUM(CASE WHEN ACTION = 'Print' AND pagecount = 3 THEN 1 ELSE 0 END) AS "print 2 PAGE"
FROM test1 GROUP BY company_name;

SQL spread column GROUP into a single row without multiple JOIN [duplicate]

If I have a MySQL table looking something like this:
company_name action pagecount
-------------------------------
Company A PRINT 3
Company A PRINT 2
Company A PRINT 3
Company B EMAIL
Company B PRINT 2
Company B PRINT 2
Company B PRINT 1
Company A PRINT 3
Is it possible to run a MySQL query to get output like this:
company_name EMAIL PRINT 1 pages PRINT 2 pages PRINT 3 pages
-------------------------------------------------------------
CompanyA 0 0 1 3
CompanyB 1 1 2 0
The idea is that pagecount can vary so the output column amount should reflect that, one column for each action/pagecount pair and then number of hits per company_name. I'm not sure if this is called a pivot table but someone suggested that?
This basically is a pivot table.
A nice tutorial on how to achieve this can be found here: http://www.artfulsoftware.com/infotree/qrytip.php?id=78
I advise reading this post and adapt this solution to your needs.
Update
After the link above is currently not available any longer I feel obliged to provide some additional information for all of you searching for mysql pivot answers in here. It really had a vast amount of information, and I won't put everything from there in here (even more since I just don't want to copy their vast knowledge), but I'll give some advice on how to deal with pivot tables the sql way generally with the example from peku who asked the question in the first place.
Maybe the link comes back soon, I'll keep an eye out for it.
The spreadsheet way...
Many people just use a tool like MSExcel, OpenOffice or other spreadsheet-tools for this purpose. This is a valid solution, just copy the data over there and use the tools the GUI offer to solve this.
But... this wasn't the question, and it might even lead to some disadvantages, like how to get the data into the spreadsheet, problematic scaling and so on.
The SQL way...
Given his table looks something like this:
CREATE TABLE `test_pivot` (
`pid` bigint(20) NOT NULL AUTO_INCREMENT,
`company_name` varchar(32) DEFAULT NULL,
`action` varchar(16) DEFAULT NULL,
`pagecount` bigint(20) DEFAULT NULL,
PRIMARY KEY (`pid`)
) ENGINE=MyISAM;
Now look into his/her desired table:
company_name EMAIL PRINT 1 pages PRINT 2 pages PRINT 3 pages
-------------------------------------------------------------
CompanyA 0 0 1 3
CompanyB 1 1 2 0
The rows (EMAIL, PRINT x pages) resemble conditions. The main grouping is by company_name.
In order to set up the conditions this rather shouts for using the CASE-statement. In order to group by something, well, use ... GROUP BY.
The basic SQL providing this pivot can look something like this:
SELECT P.`company_name`,
COUNT(
CASE
WHEN P.`action`='EMAIL'
THEN 1
ELSE NULL
END
) AS 'EMAIL',
COUNT(
CASE
WHEN P.`action`='PRINT' AND P.`pagecount` = '1'
THEN P.`pagecount`
ELSE NULL
END
) AS 'PRINT 1 pages',
COUNT(
CASE
WHEN P.`action`='PRINT' AND P.`pagecount` = '2'
THEN P.`pagecount`
ELSE NULL
END
) AS 'PRINT 2 pages',
COUNT(
CASE
WHEN P.`action`='PRINT' AND P.`pagecount` = '3'
THEN P.`pagecount`
ELSE NULL
END
) AS 'PRINT 3 pages'
FROM test_pivot P
GROUP BY P.`company_name`;
This should provide the desired result very fast. The major downside for this approach, the more rows you want in your pivot table, the more conditions you need to define in your SQL statement.
This can be dealt with, too, therefore people tend to use prepared statements, routines, counters and such.
Some additional links about this topic:
http://anothermysqldba.blogspot.de/2013/06/pivot-tables-example-in-mysql.html
http://www.codeproject.com/Articles/363339/Cross-Tabulation-Pivot-Tables-with-MySQL
http://datacharmer.org/downloads/pivot_tables_mysql_5.pdf
https://codingsight.com/pivot-tables-in-mysql/
My solution is in T-SQL without any pivots:
SELECT
CompanyName,
SUM(CASE WHEN (action='EMAIL') THEN 1 ELSE 0 END) AS Email,
SUM(CASE WHEN (action='PRINT' AND pagecount=1) THEN 1 ELSE 0 END) AS Print1Pages,
SUM(CASE WHEN (action='PRINT' AND pagecount=2) THEN 1 ELSE 0 END) AS Print2Pages,
SUM(CASE WHEN (action='PRINT' AND pagecount=3) THEN 1 ELSE 0 END) AS Print3Pages
FROM
Company
GROUP BY
CompanyName
For MySQL you can directly put conditions in SUM() function and it will be evaluated as Boolean 0 or 1 and thus you can have your count based on your criteria without using IF/CASE statements
SELECT
company_name,
SUM(action = 'EMAIL')AS Email,
SUM(action = 'PRINT' AND pagecount = 1)AS Print1Pages,
SUM(action = 'PRINT' AND pagecount = 2)AS Print2Pages,
SUM(action = 'PRINT' AND pagecount = 3)AS Print3Pages
FROM t
GROUP BY company_name
DEMO
For dynamic pivot, use GROUP_CONCAT with CONCAT.
The GROUP_CONCAT function concatenates strings from a group into one string with various options.
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(CASE WHEN action = "',
action,'" AND ',
(CASE WHEN pagecount IS NOT NULL
THEN CONCAT("pagecount = ",pagecount)
ELSE pagecount IS NULL END),
' THEN 1 ELSE 0 end) AS ',
action, IFNULL(pagecount,'')
)
)
INTO #sql
FROM
t;
SET #sql = CONCAT('SELECT company_name, ', #sql, '
FROM t
GROUP BY company_name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
DEMO HERE
A stardard-SQL version using boolean logic:
SELECT company_name
, COUNT(action = 'EMAIL' OR NULL) AS "Email"
, COUNT(action = 'PRINT' AND pagecount = 1 OR NULL) AS "Print 1 pages"
, COUNT(action = 'PRINT' AND pagecount = 2 OR NULL) AS "Print 2 pages"
, COUNT(action = 'PRINT' AND pagecount = 3 OR NULL) AS "Print 3 pages"
FROM tbl
GROUP BY company_name;
db<>fiddle here
Old sqlfiddle
How?
TRUE OR NULL yields TRUE.
FALSE OR NULL yields NULL.
NULL OR NULL yields NULL.
And COUNT only counts non-null values. Voilá.
Correct answer is:
select table_record_id,
group_concat(if(value_name='note', value_text, NULL)) as note
,group_concat(if(value_name='hire_date', value_text, NULL)) as hire_date
,group_concat(if(value_name='termination_date', value_text, NULL)) as termination_date
,group_concat(if(value_name='department', value_text, NULL)) as department
,group_concat(if(value_name='reporting_to', value_text, NULL)) as reporting_to
,group_concat(if(value_name='shift_start_time', value_text, NULL)) as shift_start_time
,group_concat(if(value_name='shift_end_time', value_text, NULL)) as shift_end_time
from other_value
where table_name = 'employee'
and is_active = 'y'
and is_deleted = 'n'
GROUP BY table_record_id
There is a tool called MySQL Pivot table generator, it can help you create a web-based pivot table that you can later export to excel(if you like). it can work if your data is in a single table or in several tables.
All you need to do is to specify the data source of the columns (it supports dynamic columns), rows, the values in the body of the table, and table relationship (if there are any)
The home page of this tool is https://mysqlreports.com/mysql-reporting-tools/mysql-pivot-table/
select t3.name, sum(t3.prod_A) as Prod_A, sum(t3.prod_B) as Prod_B, sum(t3.prod_C) as Prod_C, sum(t3.prod_D) as Prod_D, sum(t3.prod_E) as Prod_E
from
(select t2.name as name,
case when t2.prodid = 1 then t2.counts
else 0 end prod_A,
case when t2.prodid = 2 then t2.counts
else 0 end prod_B,
case when t2.prodid = 3 then t2.counts
else 0 end prod_C,
case when t2.prodid = 4 then t2.counts
else 0 end prod_D,
case when t2.prodid = "5" then t2.counts
else 0 end prod_E
from
(SELECT partners.name as name, sales.products_id as prodid, count(products.name) as counts
FROM test.sales left outer join test.partners on sales.partners_id = partners.id
left outer join test.products on sales.products_id = products.id
where sales.partners_id = partners.id and sales.products_id = products.id group by partners.name, prodid) t2) t3
group by t3.name ;
One option would be combining use of CASE..WHEN statement is redundant within an aggregation for MySQL Database, and considering the needed query generation dynamically along with getting proper column title for the result set as in the following code block :
SET #sql = NULL;
SELECT GROUP_CONCAT(
CONCAT('SUM( `action` = ''', action, '''',pc0,' ) AS ',action,pc1)
)
INTO #sql
FROM
(
SELECT DISTINCT `action`,
IF(`pagecount` IS NULL,'',CONCAT('page',`pagecount`)) AS pc1,
IF(`pagecount` IS NULL,'',CONCAT(' AND `pagecount` = ', pagecount, '')) AS pc0
FROM `tab`
ORDER BY CONCAT(action,pc0)
) t;
SET #sql = CONCAT('SELECT company_name,',#sql,' FROM `tab` GROUP BY company_name');
SELECT #sql;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Demo
SELECT company_name, SUM(CASE WHEN ACTION = 'Email' THEN 1 ELSE 0 END) AS "Email",
SUM(CASE WHEN ACTION = 'Print' AND pagecount = 1 THEN 1 ELSE 0 END) AS "print 1 PAGE",
SUM(CASE WHEN ACTION = 'Print' AND pagecount = 2 THEN 1 ELSE 0 END) AS "print 2 PAGE",
SUM(CASE WHEN ACTION = 'Print' AND pagecount = 3 THEN 1 ELSE 0 END) AS "print 2 PAGE"
FROM test1 GROUP BY company_name;

listagg in SQL to group rows in to one row

i have a table 1 shown below
Name role F1 status1 status 2
sam player yes null null
sam admin yes null null
sam guest no x x
i want the result to be
Name role status1 status 2
sam admin,player x x
i have done a query to list_agg the role in to one row.but the status is null for sam to show when F1='yes'
query i used
select name,list_agg(role,',') within group(order by name),max(status1),max(status2)
from table 1 where F1='yes'
group by name
but i get something like this
name role status1 status2
sam admin,player null null
i want the where to work only on role column and the max(status1) to be in status1 i.e.'x'.please help me .thank you
You can try using LISTAGG() within a GROUP BY query:
SELECT Name,
LISTAGG(Role, ',') WITHIN GROUP (ORDER BY Role) "Role"
MAX(CASE WHEN water_access = 'Y' THEN 'Y' ELSE NULL END) "water_access",
MAX(CASE WHEN food_access = 'Y' THEN 'Y' ELSE NULL END) "food_access",
MAX(CASE WHEN power_access = 'Y' THEN 'Y' ELSE NULL END) "power_access"
FROM yourTable
GROUP BY Name
ORDER BY Name DESC
Note that I chose to order the aggregation of each Name group using the Role, because you didn't provide us with any column which could give the ordering you show in your expected output.
Second note: MAX() in Oracle ignores NULL values, so it can be used in the pivot to correctly identify the Y values you want to appear.
try this out...
select * from table_name pivot(sum(name) for role

Update column in set of records only if multiple rows exist with a given value in a different field?

My_Table would be something like this:
user_id shared_field bool_field
------- ------------ ----------
1 abc null
2 def null
3 ghi Y
4 ghi null
5 ghi null
6 abc Y
7 jkl null
If the bool_field changes for a user who shares the same shared_field with other users (such as user_id 3, 4, and 5 above), only that one user should have a 'Y'. The rest should have null values in the bool_field column. For example, if user_id 4 should now have the 'Y', I have to change user_id 4's bool_field to 'Y', and ensure that user_id 3 and 5 have bool_field values of null.
If the user doesn't share a shared_field value with anyone else, then that bool_field should be null (as in user_id 1 and 2 above).
Update: added a couple of lines to show that multiple user_ids could share a given shared_field (eg, 1 and 6 both have 'abc'; 3, 4, and 5 all have 'ghi' - only one 'abc' user should have a 'Y', and only one 'ghi' user should have a 'Y' and so on, while the rest have null in their bool_field column; user_ids that don't share a shared_field value, such as user_ids 2 and 7, should all have null in their bool_field column.) Clear as mud, right? ;)
This statement works:
UPDATE my_table
SET bool_field = (CASE
WHEN user_id = 4 THEN 'Y'
ELSE NULL
END)
WHERE shared_field = 'ghi'
AND (SELECT COUNT(shared_field)
FROM my_table
WHERE shared_field = 'ghi') > 1;
The question: is there some way that I can accomplish this same thing without knowing the shared_field value in advance? For example (and this doesn't work, of course) - Update: "of course" means I know this doesn't work because it is not correct Oracle syntax! The point is to give an idea of what I'm trying to do.
UPDATE my_table
SET bool_field = (CASE
WHEN user_id = 4 THEN 'Y'
ELSE NULL
END)
WHERE shared_field = (SELECT shared_field FROM my_table WHERE user_id = 4) as sharedVal
AND (SELECT COUNT(shared_field)
FROM my_table
WHERE shared_field = sharedVal) > 1;
Update: this is a regular SQL statement - I can't use a stored procedure.
First, rather than saying that something "doesn't work", it is generally helpful to tell us how it doesn't work. The query you posted, for example, appears to have syntax errors (as sharedVal is invalid because you can't assign an alias to an expression you're computing in the SELECT list). But it's not clear if "doesn't work" means that you're getting syntax errors (which we can relatively easily debug with the error message) or whether it means that the query runs but doesn't do what you want (which I would expect the query to do if the syntax errors were corrected) in which case knowing how the query isn't doing what you want would be helpful.
I would expect something like
UPDATE my_table a
SET bool_field = (CASE WHEN user_id = 4
THEN 'Y'
ELSE NULL
END)
WHERE shared_field = (SELECT shared_field
FROM my_table b
WHERE b.user_id = 4)
AND EXISTS( SELECT 1
FROM my_table c
WHERE a.shared_field = c.shared_field
AND a.user_id != c.user_id )
to work assuming that user_id is the primary key.
It's been awhile since I've worked on Oracle, so check to make sure that this query returns the list of records you want to update:
SELECT *
FROM my_table m
WHERE EXISTS (SELECT 1
FROM my_table
WHERE shared_field = m.shared_field
HAVING COUNT(shared_field) > 1);
If so, then this should work:
UPDATE my_table
SET bool_field = (CASE
WHEN user_id = 4 THEN 'Y'
ELSE NULL
END)
WHERE EXISTS (SELECT 1
FROM my_table
WHERE shared_field = m.shared_field
HAVING COUNT(shared_field) > 1);
You could also try this WHERE clause:
WHERE shared_field IN (SELECT shared_field
FROM my_table
GROUP BY shared_field
HAVING COUNT(shared_field) > 1);

Replace NULL with values

Here is my challenge:
I have a log table which every time a record is changed adds a new record but puts a NULL value for each non-changed value in each record. In other words only the changed value is set, the rest unchanged fields in each row simply has a NULL value.
Now I would like to replace each NULL value with the value above it that is NOT a NULL value like below:
Source table: Task_log
ID Owner Status Flag
1 Bob Registrar T
2 Sue NULL NULL
3 NULL NULL F
4 Frank Admission T
5 NULL NULL F
6 NULL NULL T
Desired output table: Task_log
ID Owner Status Flag
1 Bob Registrar T
2 Sue Registrar T
3 Sue Registrar F
4 Frank Admission T
5 Frank Admission F
6 Frank Admission T
How do I write a query which will generate the desired output table?
One the new windowed function of SQLServer 2012 is FIRST_VALUE, wich have quite a direct name, it can be partitioned through the OVER clause, before using it is necessary to divide every column in data block, a block for a column begin when a value is found.
With Block As (
Select ID
, Owner
, OBlockID = SUM(Case When Owner Is Null Then 0 Else 1 End)
OVER (ORDER BY ID)
, Status
, SBlockID = SUM(Case When Status Is Null Then 0 Else 1 End)
OVER (ORDER BY ID)
, Flag
, FBlockID = SUM(Case When Flag Is Null Then 0 Else 1 End)
OVER (ORDER BY ID)
From Task_log
)
Select ID
, Owner = FIRST_VALUE(Owner) OVER (PARTITION BY OBlockID ORDER BY ID)
, Status = FIRST_VALUE(Status) OVER (PARTITION BY SBlockID ORDER BY ID)
, Flag = FIRST_VALUE(Flag) OVER (PARTITION BY FBlockID ORDER BY ID)
FROM Block
SQLFiddle demo
The UPDATE query is easily derived
As I mentioned in my comment, I would try to fix the process that is creating the records rather than fixing the junk data. If that is not an option, the code below should get you pointed in the right direction.
UPDATE t1
set t1.owner = COALESCE(t1.owner, t2.owner),
t1.Status = COALESCE(t1.status, t2.status),
t1.Flag = COALESCE(t1.flag, t2.flag)
FROM Task_log as t1
INNER JOIN Task_log as t2
ON t1.id = (t1.id + 1)
where t1.owner is null
OR t1.status is null
OR t1.flag is null
I can think of several approaches.
You could use a combination of COALESCE with an array aggregate function. Unfortunately it doesn't look like SQL Server supports array_agg natively (although some nice people have developed some workarounds).
You could also use a subselect for each column.
SELECT id,
(SELECT TOP 1 FROM (SELECT owner FROM ... WHERE id = outer_id AND owner IS NOT NULL order by ID desc )) AS owner,
-- other columns
You could probably do something with window functions, too.
A vanilla solution would be:
select id
, owner
, coalesce(owner, ( select owner from t t2
where id = (select max(id) from t t3
where id < t1.id and owner is not null))
) as new_owner
, flag
, coalesce(flag, ( select flag from t t2
where id = (select max(id) from t t3
where id < t1.id and flag is not null))
) as new_flag
from t t1
Rather inefficient, but should work on most DBMS